Compare commits

...

88 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
81 changed files with 51854 additions and 861 deletions

2
.github/FUNDING.yml vendored
View File

@ -2,5 +2,5 @@
github: Phoenix616 github: Phoenix616
custom: ['https://acrobot-paypal.phoenix616.dev', 'https://tip.phoenix616.dev'] custom: ['https://acrobot-paypal.phoenix616.dev', 'https://tip.phoenix616.dev']
patreon: Phoenix616 issuehunt: ChestShop-authors/ChestShop-3
ko_fi: Phoenix616 ko_fi: Phoenix616

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,5 +34,7 @@ Links
* [Forum Thread](http://forums.bukkit.org/threads/4150/) * [Forum Thread](http://forums.bukkit.org/threads/4150/)
* [BukkitDev site](http://dev.bukkit.org/projects/chestshop/) * [BukkitDev site](http://dev.bukkit.org/projects/chestshop/)
* [SpigotMC site](https://www.spigotmc.org/resources/chestshop.51856/) * [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) * [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!)

68
pom.xml
View File

@ -6,7 +6,7 @@
<groupId>com.acrobot.chestshop</groupId> <groupId>com.acrobot.chestshop</groupId>
<artifactId>chestshop</artifactId> <artifactId>chestshop</artifactId>
<version>3.12.1</version> <version>3.12.3-SNAPSHOT</version>
<description>Chest-and-sign shop plugin for Bukkit</description> <description>Chest-and-sign shop plugin for Bukkit</description>
<name>ChestShop</name> <name>ChestShop</name>
@ -18,8 +18,8 @@
<repositories> <repositories>
<repository> <repository>
<id>sk89q-repo</id> <id>enginehub-repo</id>
<url>http://maven.sk89q.com/repo/</url> <url>https://maven.enginehub.org/repo/</url>
</repository> </repository>
<repository> <repository>
<id>codemc-repo</id> <id>codemc-repo</id>
@ -31,7 +31,7 @@
</repository> </repository>
<repository> <repository>
<id>vault-repo</id> <id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases/</url> <url>https://nexus.hc.to/content/repositories/pub_releases/</url>
</repository> </repository>
<repository> <repository>
<id>minebench-repo</id> <id>minebench-repo</id>
@ -41,14 +41,6 @@
<id>local_repo</id> <id>local_repo</id>
<url>file://${project.basedir}/repo/</url> <url>file://${project.basedir}/repo/</url>
</repository> </repository>
<repository>
<id>reserve-repo</id>
<url>https://dl.bintray.com/theneweconomy/java/</url>
</repository>
<repository>
<id>redprotect-repo</id>
<url>https://raw.githubusercontent.com/FabioZumbi12/RedProtect/mvn-repo/</url>
</repository>
<repository> <repository>
<id>jitpack.io</id> <id>jitpack.io</id>
<url>https://jitpack.io</url> <url>https://jitpack.io</url>
@ -57,20 +49,28 @@
<id>NyaaCat</id> <id>NyaaCat</id>
<url>https://ci.nyaacat.com/maven/</url> <url>https://ci.nyaacat.com/maven/</url>
</repository> </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> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.13.1</version> <version>4.13.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.j256.ormlite</groupId> <groupId>com.j256.ormlite</groupId>
<artifactId>ormlite-jdbc</artifactId> <artifactId>ormlite-jdbc</artifactId>
<version>5.1</version> <version>6.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@ -84,21 +84,21 @@
<dependency> <dependency>
<groupId>de.themoep</groupId> <groupId>de.themoep</groupId>
<artifactId>minedown-adventure</artifactId> <artifactId>minedown-adventure</artifactId>
<version>1.7.1-SNAPSHOT</version> <version>1.7.2-SNAPSHOT</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.kyori</groupId> <groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId> <artifactId>adventure-platform-bukkit</artifactId>
<version>4.0.1</version> <version>4.3.2</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.kyori</groupId> <groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId> <artifactId>adventure-text-serializer-gson</artifactId>
<version>4.9.1</version> <version>4.14.0</version>
<scope>compile</scope> <scope>compile</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -112,14 +112,14 @@
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId> <artifactId>log4j-core</artifactId>
<version>2.17.1</version> <version>2.17.2</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.bstats</groupId> <groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId> <artifactId>bstats-bukkit</artifactId>
<version>1.7</version> <version>3.0.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@ -157,7 +157,7 @@
<dependency> <dependency>
<groupId>fr.xephi</groupId> <groupId>fr.xephi</groupId>
<artifactId>authme</artifactId> <artifactId>authme</artifactId>
<version>5.5.0-SNAPSHOT</version> <version>5.6.0-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -220,6 +220,12 @@
<artifactId>worldedit-core</artifactId> <artifactId>worldedit-core</artifactId>
<version>7.0.0-SNAPSHOT</version> <version>7.0.0-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
@ -248,16 +254,22 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.github.jojodmo</groupId>
<artifactId>ItemBridge</artifactId>
<version>b0054538c1</version>
</dependency>
<dependency> <dependency>
<groupId>br.net.fabiozumbi12.RedProtect</groupId> <groupId>br.net.fabiozumbi12.RedProtect</groupId>
<artifactId>RedProtect-Spigot</artifactId> <artifactId>RedProtect-Spigot</artifactId>
<version>7.6.2</version> <version>7.7.3</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>br.net.fabiozumbi12.RedProtect</groupId> <groupId>br.net.fabiozumbi12.RedProtect</groupId>
<artifactId>RedProtect-Core</artifactId> <artifactId>RedProtect-Core</artifactId>
<version>7.6.2</version> <version>7.7.3</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -329,7 +341,7 @@
<dependency> <dependency>
<groupId>net.tnemc</groupId> <groupId>net.tnemc</groupId>
<artifactId>Reserve</artifactId> <artifactId>Reserve</artifactId>
<version>0.1.0.10</version> <version>0.1.5.4</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -358,6 +370,7 @@
<Distribution-Type>${buildType}</Distribution-Type> <Distribution-Type>${buildType}</Distribution-Type>
<Built-At>${maven.build.timestamp}</Built-At> <Built-At>${maven.build.timestamp}</Built-At>
<Build-Jdk>${java.runtime.version}</Build-Jdk> <Build-Jdk>${java.runtime.version}</Build-Jdk>
<paperweight-mappings-namespace>mojang</paperweight-mappings-namespace>
</manifestEntries> </manifestEntries>
</archive> </archive>
</configuration> </configuration>
@ -448,6 +461,7 @@
<build.number>${buildNumber}</build.number> <build.number>${buildNumber}</build.number>
<user.name>${buildType}</user.name> <user.name>${buildType}</user.name>
<bukkit.plugin.version>${project.version} ${buildDescription}</bukkit.plugin.version> <bukkit.plugin.version>${project.version} ${buildDescription}</bukkit.plugin.version>
<bukkit.dependency.version>1.20.5-R0.1-SNAPSHOT</bukkit.dependency.version>
</properties> </properties>
<profiles> <profiles>
@ -461,14 +475,14 @@
<repositories> <repositories>
<repository> <repository>
<id>paper-repo</id> <id>paper-repo</id>
<url>https://papermc.io/repo/repository/maven-public/</url> <url>https://repo.papermc.io/repository/maven-public/</url>
</repository> </repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>io.papermc.paper</groupId> <groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId> <artifactId>paper-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version> <version>${bukkit.dependency.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@ -492,7 +506,7 @@
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.13.2-R0.1-SNAPSHOT</version> <version>${bukkit.dependency.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@ -522,7 +536,7 @@
<repositories> <repositories>
<repository> <repository>
<id>paper-repo</id> <id>paper-repo</id>
<url>https://papermc.io/repo/repository/maven-public/</url> <url>https://repo.papermc.io/repository/maven-public/</url>
</repository> </repository>
</repositories> </repositories>
<dependencies> <dependencies>

View File

@ -1,20 +1,21 @@
package com.Acrobot.Breeze.Collection; package com.Acrobot.Breeze.Collection;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
public class SimpleCache<K, V> { public class SimpleCache<K, V> {
private final LinkedHashMap<K, V> map; private final Map<K, V> map;
public SimpleCache(int cacheSize) { 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 @Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > cacheSize; return size() > cacheSize;
} }
}; });
} }
public V put(K key, V value) { public V put(K key, V value) {

View File

@ -126,7 +126,7 @@ public class Configuration {
return lastLine.isEmpty(); return lastLine.isEmpty();
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); Logger.getLogger("Configuration").log(Level.SEVERE, "Error while checking if file " + file.getName() + " ends with space", e);
return false; return false;
} }
} }

View File

@ -1,9 +1,10 @@
package com.Acrobot.Breeze.Configuration; package com.Acrobot.Breeze.Configuration;
import com.Acrobot.Breeze.Configuration.Annotations.ConfigurationComment; import com.Acrobot.Breeze.Configuration.Annotations.ConfigurationComment;
import com.Acrobot.Breeze.Configuration.Annotations.Parser;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* @author Acrobot * @author Acrobot
@ -29,7 +30,7 @@ public class FieldParser {
try { try {
builder.append(field.getName()).append(": ").append(parser.parseToYAML(field.get(null))); builder.append(field.getName()).append(": ").append(parser.parseToYAML(field.get(null)));
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
e.printStackTrace(); Logger.getLogger("FieldParser").log(Level.SEVERE, "Error while parsing field", e);
return ""; return "";
} }

View File

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

View File

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

View File

@ -132,7 +132,7 @@ public class Table {
String statement; String statement;
if (condition == null || condition.isEmpty()) { 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); statement = String.format(INSERT_VALUES, format);
} else { } else {
String format = row.getKeysAndValues().entrySet().stream() String format = row.getKeysAndValues().entrySet().stream()

View File

@ -1,5 +1,9 @@
package com.Acrobot.Breeze.Utils.Encoding; 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>Encodes and decodes to and from Base64 notation.</p>
* <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p> * <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
@ -559,59 +563,31 @@ public class Base64 {
throw new NullPointerException("Cannot serialize a null object."); throw new NullPointerException("Cannot serialize a null object.");
} // end if: null } // end if: null
// Streams // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
java.io.ByteArrayOutputStream baos = null; try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
java.io.OutputStream b64os = null; java.io.OutputStream b64os = new Base64.OutputStream(baos, ENCODE | options)) {
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);
if ((options & GZIP) != 0) { if ((options & GZIP) != 0) {
// Gzip // Gzip
gzos = new java.util.zip.GZIPOutputStream(b64os); try (java.util.zip.GZIPOutputStream gzos = new java.util.zip.GZIPOutputStream(b64os);
oos = new java.io.ObjectOutputStream(gzos); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(gzos)){
oos.writeObject(serializableObject);
}
} else { } else {
// Not gzipped // 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 } // 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 } // end encode
@ -635,7 +611,6 @@ public class Base64 {
} catch (java.io.IOException ex) { } catch (java.io.IOException ex) {
assert false : ex.getMessage(); assert false : ex.getMessage();
} // end catch } // end catch
assert encoded != null;
return encoded; return encoded;
} // end encodeBytes } // end encodeBytes
@ -700,7 +675,6 @@ public class Base64 {
} catch (java.io.IOException ex) { } catch (java.io.IOException ex) {
assert false : ex.getMessage(); assert false : ex.getMessage();
} // end catch } // end catch
assert encoded != null;
return encoded; return encoded;
} // end encodeBytes } // end encodeBytes
@ -810,40 +784,13 @@ public class Base64 {
// Compress? // Compress?
if ((options & GZIP) != 0) { if ((options & GZIP) != 0) {
java.io.ByteArrayOutputStream baos = null; try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
java.util.zip.GZIPOutputStream gzos = null; Base64.OutputStream b64os = new Base64.OutputStream(baos, ENCODE | options);
Base64.OutputStream b64os = null; java.util.zip.GZIPOutputStream gzos = new java.util.zip.GZIPOutputStream(b64os)) {
try {
// GZip -> Base64 -> ByteArray
baos = new java.io.ByteArrayOutputStream();
b64os = new Base64.OutputStream(baos, ENCODE | options);
gzos = new java.util.zip.GZIPOutputStream(b64os);
gzos.write(source, off, len); gzos.write(source, off, len);
gzos.close(); return baos.toByteArray();
} // end try } // 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 } // end if: compress
// Else, don't compress. Better not to use streams at all then. // Else, don't compress. Better not to use streams at all then.
@ -1162,20 +1109,16 @@ public class Base64 {
// Check to see if it's gzip-compressed // Check to see if it's gzip-compressed
// GZIP Magic Two-Byte Number: 0x8b1f (35615) // GZIP Magic Two-Byte Number: 0x8b1f (35615)
boolean dontGunzip = (options & DONT_GUNZIP) != 0; 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); int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) { 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]; byte[] buffer = new byte[2048];
int length = 0; int length = 0;
try { try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
baos = new java.io.ByteArrayOutputStream(); java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(bytes);
bais = new java.io.ByteArrayInputStream(bytes); java.util.zip.GZIPInputStream gzis = new java.util.zip.GZIPInputStream(bais)) {
gzis = new java.util.zip.GZIPInputStream(bais);
while ((length = gzis.read(buffer)) >= 0) { while ((length = gzis.read(buffer)) >= 0) {
baos.write(buffer, 0, length); baos.write(buffer, 0, length);
@ -1184,25 +1127,10 @@ public class Base64 {
// No error? Get new bytes. // No error? Get new bytes.
bytes = baos.toByteArray(); bytes = baos.toByteArray();
} // end try } catch (java.io.IOException e) {
catch (java.io.IOException e) { Logger.getLogger("Base64").log(Level.SEVERE, "Unable to decode", e);
e.printStackTrace();
// Just return originally-decoded bytes // 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: gzipped
} // end if: bytes.length >= 2 } // end if: bytes.length >= 2
@ -1252,55 +1180,30 @@ public class Base64 {
// Decode and gunzip if necessary // Decode and gunzip if necessary
byte[] objBytes = decode(encodedObject, options); byte[] objBytes = decode(encodedObject, options);
java.io.ByteArrayInputStream bais = null; try (java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(objBytes)) {
java.io.ObjectInputStream ois = null;
Object obj = null;
try {
bais = new java.io.ByteArrayInputStream(objBytes);
// If no custom class loader is provided, use Java's builtin OIS. // If no custom class loader is provided, use Java's builtin OIS.
if (loader == null) { 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 } // end if: no loader provided
// Else make a customized object input stream that uses // Else make a customized object input stream that uses
// the provided class loader. // the provided class loader.
else { else {
ois = new java.io.ObjectInputStream(bais) { try (java.io.ObjectInputStream ois = new java.io.ObjectInputStream(bais) {
@Override @Override
public Class<?> resolveClass(java.io.ObjectStreamClass streamClass) public Class<?> resolveClass(java.io.ObjectStreamClass streamClass)
throws java.io.IOException, ClassNotFoundException { throws java.io.IOException, ClassNotFoundException {
Class<?> c = Class.forName(streamClass.getName(), false, loader); return 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
} // end resolveClass } // end resolveClass
}; // end ois }) { // end ois
return ois.readObject();
} // end try
} // end else: no custom class loader } // end else: no custom class loader
obj = ois.readObject();
} // end try } // 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 } // end decodeObject
@ -1325,17 +1228,10 @@ public class Base64 {
throw new NullPointerException("Data to encode was null."); throw new NullPointerException("Data to encode was null.");
} // end iff } // end iff
Base64.OutputStream bos = null; try (OutputStream bos = new OutputStream(
try { new java.io.FileOutputStream(filename), Base64.ENCODE)) {
bos = new Base64.OutputStream(
new java.io.FileOutputStream(filename), Base64.ENCODE);
bos.write(dataToEncode); bos.write(dataToEncode);
} finally { }
try {
bos.close();
} catch (Exception e) {
}
} // end finally
} // end encodeToFile } // end encodeToFile
@ -1356,21 +1252,10 @@ public class Base64 {
public static void decodeToFile(String dataToDecode, String filename) public static void decodeToFile(String dataToDecode, String filename)
throws java.io.IOException { throws java.io.IOException {
Base64.OutputStream bos = null; try (OutputStream bos = new OutputStream(
try { new java.io.FileOutputStream(filename), Base64.DECODE)) {
bos = new Base64.OutputStream(
new java.io.FileOutputStream(filename), Base64.DECODE);
bos.write(dataToDecode.getBytes(PREFERRED_ENCODING)); 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 } // end decodeToFile
@ -1393,24 +1278,22 @@ public class Base64 {
throws java.io.IOException { throws java.io.IOException {
byte[] decodedData = null; byte[] decodedData = null;
Base64.InputStream bis = null; // Set up some useful variables
try { java.io.File file = new java.io.File(filename);
// Set up some useful variables byte[] buffer = null;
java.io.File file = new java.io.File(filename); int length = 0;
byte[] buffer = null; int numBytes = 0;
int length = 0;
int numBytes = 0;
// Check for size of file // Check for size of file
if (file.length() > Integer.MAX_VALUE) { if (file.length() > Integer.MAX_VALUE) {
throw new java.io.IOException("File is too big for this convenience method (" + file.length() + " bytes)."); throw new java.io.IOException("File is too big for this convenience method (" + file.length() + " bytes).");
} // end if: file too big for int index } // end if: file too big for int index
buffer = new byte[(int) file.length()]; buffer = new byte[(int) file.length()];
// Open a stream // Open a stream
bis = new Base64.InputStream( try (Base64.InputStream bis = new Base64.InputStream(
new java.io.BufferedInputStream( new java.io.BufferedInputStream(
new java.io.FileInputStream(file)), Base64.DECODE); new java.io.FileInputStream(file)), Base64.DECODE)) {
// Read until done // Read until done
while ((numBytes = bis.read(buffer, length, 4096)) >= 0) { while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
@ -1420,17 +1303,7 @@ public class Base64 {
// Save in a variable to return // Save in a variable to return
decodedData = new byte[length]; decodedData = new byte[length];
System.arraycopy(buffer, 0, decodedData, 0, length); System.arraycopy(buffer, 0, decodedData, 0, length);
} // end try
} // 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 decodedData; return decodedData;
} // end decodeFromFile } // end decodeFromFile
@ -1454,18 +1327,16 @@ public class Base64 {
throws java.io.IOException { throws java.io.IOException {
String encodedData = null; String encodedData = null;
Base64.InputStream bis = null; // Set up some useful variables
try { java.io.File file = new java.io.File(filename);
// Set up some useful variables 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)
java.io.File file = new java.io.File(filename); int length = 0;
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 numBytes = 0;
int length = 0;
int numBytes = 0;
// Open a stream // Open a stream
bis = new Base64.InputStream( try (Base64.InputStream bis = new Base64.InputStream(
new java.io.BufferedInputStream( new java.io.BufferedInputStream(
new java.io.FileInputStream(file)), Base64.ENCODE); new java.io.FileInputStream(file)), Base64.ENCODE)) {
// Read until done // Read until done
while ((numBytes = bis.read(buffer, length, 4096)) >= 0) { while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
@ -1476,15 +1347,6 @@ public class Base64 {
encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING); encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
} // end try } // 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; return encodedData;
} // end encodeFromFile } // end encodeFromFile
@ -1501,21 +1363,10 @@ public class Base64 {
throws java.io.IOException { throws java.io.IOException {
String encoded = Base64.encodeFromFile(infile); String encoded = Base64.encodeFromFile(infile);
java.io.OutputStream out = null; try (java.io.OutputStream out = new java.io.BufferedOutputStream(
try { new java.io.FileOutputStream(outfile))) {
out = new java.io.BufferedOutputStream( out.write(encoded.getBytes(StandardCharsets.US_ASCII)); // Strict, 7-bit output.
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
} // end encodeFileToFile } // end encodeFileToFile
@ -1531,21 +1382,10 @@ public class Base64 {
throws java.io.IOException { throws java.io.IOException {
byte[] decoded = Base64.decodeFromFile(infile); byte[] decoded = Base64.decodeFromFile(infile);
java.io.OutputStream out = null; try (java.io.OutputStream out = new java.io.BufferedOutputStream(
try { new java.io.FileOutputStream(outfile))) {
out = new java.io.BufferedOutputStream(
new java.io.FileOutputStream(outfile));
out.write(decoded); 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 } // end decodeFileToFile

View File

@ -351,7 +351,7 @@ public class InventoryUtil {
itemList.add(item.clone()); itemList.add(item.clone());
} }
return itemList.toArray(new ItemStack[itemList.size()]); return itemList.toArray(new ItemStack[0]);
} }
/** /**
@ -413,7 +413,7 @@ public class InventoryUtil {
stackedItems.add(item.clone()); stackedItems.add(item.clone());
continue; 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(); ItemStack itemClone = item.clone();
itemClone.setAmount(maxStackSize); itemClone.setAmount(maxStackSize);
stackedItems.add(itemClone); stackedItems.add(itemClone);
@ -424,6 +424,6 @@ public class InventoryUtil {
stackedItems.add(rest); stackedItems.add(rest);
} }
} }
return stackedItems.toArray(new ItemStack[stackedItems.size()]); return stackedItems.toArray(new ItemStack[0]);
} }
} }

View File

@ -25,7 +25,6 @@ import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.nodes.Tag;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -54,6 +53,23 @@ public class MaterialUtil {
// 15 dashes fit on one sign line with the default resource pack: // 15 dashes fit on one sign line with the default resource pack:
public static final int MAXIMUM_SIGN_WIDTH = (short) getMinecraftStringWidth("---------------"); 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 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 final Yaml YAML = new Yaml(new YamlBukkitConstructor(), new YamlRepresenter(), new DumperOptions());
@ -146,7 +162,15 @@ public class MaterialUtil {
* @return Material found * @return Material found
*/ */
public static Material getMaterial(String name) { public static Material getMaterial(String name) {
String formatted = name.replaceAll("(?<!^)([A-Z1-9])", "_$1").replace(' ', '_').toUpperCase(Locale.ROOT); 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); Material material = MATERIAL_CACHE.get(formatted);
if (material != null) { if (material != null) {
@ -160,7 +184,7 @@ public class MaterialUtil {
return material; return material;
} }
material = new EnumParser<Material>().parse(name, Material.values()); material = new EnumParser<Material>().parse(replacedName, Material.values());
if (material != null) { if (material != null) {
MATERIAL_CACHE.put(formatted, material); MATERIAL_CACHE.put(formatted, material);
} }
@ -264,17 +288,42 @@ public class MaterialUtil {
* @return The name shortened to the max length * @return The name shortened to the max length
*/ */
public static String getShortenedName(String itemName, int maxWidth) { public static String getShortenedName(String itemName, int maxWidth) {
itemName = StringUtil.capitalizeFirstLetter(itemName.replace('_', ' '), ' '); // Restore spaces in string that might be already be shortened
int width = getMinecraftStringWidth(itemName); String name = itemName.replaceAll("([a-z])([A-Z1-9])", "$1 $2");
name = StringUtil.capitalizeFirstLetter(name.replace('_', ' '), ' ');
int width = getMinecraftStringWidth(name);
if (width <= maxWidth) { if (width <= maxWidth) {
return itemName; return name;
} }
String[] itemParts = itemName.split("[ \\-]"); String[] itemParts = name.split("[ \\-]");
itemName = String.join("", itemParts); String noSpaceName = String.join("", itemParts);
width = getMinecraftStringWidth(itemName); width = getMinecraftStringWidth(noSpaceName);
if (width <= maxWidth) { if (width <= maxWidth) {
return itemName; 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 exceeding = width - maxWidth;
int shortestIndex = 0; int shortestIndex = 0;
int longestIndex = 0; int longestIndex = 0;
@ -402,17 +451,18 @@ public class MaterialUtil {
private static class EnumParser<E extends Enum<E>> { private static class EnumParser<E extends Enum<E>> {
private E parse(String name, E[] values) { private E parse(String name, E[] values) {
String formatted = name.replaceAll("(?<!^)(?>\\s?)([A-Z1-9])", "_$1").toUpperCase(Locale.ROOT).replace(' ', '_');
try { try {
return E.valueOf(values[0].getDeclaringClass(), name.toUpperCase(Locale.ROOT)); return E.valueOf(values[0].getDeclaringClass(), formatted);
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
E currentEnum = null; List<E> possibleEnums = new ArrayList<>();
String[] typeParts = name.replaceAll("(?<!^)([A-Z1-9])", "_$1").toUpperCase(Locale.ROOT).split("[ _]"); String[] typeParts = formatted.split("_");
int length = Short.MAX_VALUE; int length = Short.MAX_VALUE;
for (E e : values) { for (E e : values) {
String enumName = e.name(); String enumName = e.name();
if (enumName.length() < length && enumName.startsWith(name)) { if (enumName.length() < length && enumName.startsWith(formatted)) {
length = (short) enumName.length(); length = enumName.length();
currentEnum = e; possibleEnums.add(e);
} else if (typeParts.length > 1) { } else if (typeParts.length > 1) {
String[] nameParts = enumName.split("_"); String[] nameParts = enumName.split("_");
if (typeParts.length == nameParts.length) { if (typeParts.length == nameParts.length) {
@ -424,13 +474,28 @@ public class MaterialUtil {
} }
} }
if (matched) { if (matched) {
currentEnum = e; possibleEnums.add(e);
break;
} }
} }
} }
} }
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;
} }
} }
} }

View File

@ -1,10 +1,13 @@
package com.Acrobot.Breeze.Utils; package com.Acrobot.Breeze.Utils;
import org.apache.commons.lang.WordUtils;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
/** /**
* @author Acrobot * @author Acrobot
@ -22,9 +25,13 @@ public class StringUtil {
if (string == null || string.isEmpty()) { if (string == null || string.isEmpty()) {
return string; return string;
} }
char[] separators = new char[]{separator};
return WordUtils.capitalize(string.toLowerCase(Locale.ROOT), 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(" "));
} }
/** /**
@ -116,4 +123,52 @@ public class StringUtil {
} }
return width; 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

@ -66,6 +66,12 @@ import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.Message;
import org.bstats.bukkit.Metrics; 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.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Server; import org.bukkit.Server;
@ -83,8 +89,12 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.UUID; 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.jar.JarFile;
import java.util.logging.FileHandler; import java.util.logging.FileHandler;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -99,6 +109,9 @@ public class ChestShop extends JavaPlugin {
private static ChestShop plugin; private static ChestShop plugin;
private static Server server; private static Server server;
private static PluginDescriptionFile description; private static PluginDescriptionFile description;
private static final ExecutorService executorService = Executors.newCachedThreadPool();
private static Metrics bStats;
private static BukkitAudiences audiences; private static BukkitAudiences audiences;
@ -106,6 +119,7 @@ public class ChestShop extends JavaPlugin {
private static ItemDatabase itemDatabase; private static ItemDatabase itemDatabase;
private static Logger logger; private static Logger logger;
private static Logger shopLogger;
private FileHandler handler; private FileHandler handler;
private List<PluginCommand> commands = new ArrayList<>(); private List<PluginCommand> commands = new ArrayList<>();
@ -113,6 +127,8 @@ public class ChestShop extends JavaPlugin {
public ChestShop() { public ChestShop() {
dataFolder = getDataFolder(); dataFolder = getDataFolder();
logger = getLogger(); logger = getLogger();
shopLogger = Logger.getLogger("ChestShop Shops");
shopLogger.setParent(logger);
description = getDescription(); description = getDescription();
server = getServer(); server = getServer();
plugin = this; plugin = this;
@ -125,6 +141,7 @@ public class ChestShop extends JavaPlugin {
@Override @Override
public void onEnable() { public void onEnable() {
bStats = new Metrics(this, 1109);
audiences = BukkitAudiences.create(this); audiences = BukkitAudiences.create(this);
turnOffDatabaseLogging(); turnOffDatabaseLogging();
if (!handleMigrations()) { if (!handleMigrations()) {
@ -152,20 +169,6 @@ public class ChestShop extends JavaPlugin {
registerPluginMessagingChannels(); registerPluginMessagingChannels();
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);
}
startStatistics(); startStatistics();
startBuildNotificatier(); startBuildNotificatier();
startUpdater(); startUpdater();
@ -173,9 +176,11 @@ public class ChestShop extends JavaPlugin {
private void registerCommand(String name, CommandExecutor executor, Permission permission) { private void registerCommand(String name, CommandExecutor executor, Permission permission) {
PluginCommand command = getCommand(name); PluginCommand command = getCommand(name);
command.setExecutor(executor); if (command != null) {
command.setPermission(permission.toString()); command.setExecutor(executor);
commands.add(command); command.setPermission(permission.toString());
commands.add(command);
}
} }
public void loadConfig() { public void loadConfig() {
@ -186,6 +191,22 @@ public class ChestShop extends JavaPlugin {
NameManager.load(); NameManager.load();
commands.forEach(c -> c.setPermissionMessage(Messages.ACCESS_DENIED.getTextWithPrefix(null))); 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() { private void turnOffDatabaseLogging() {
@ -238,7 +259,7 @@ public class ChestShop extends JavaPlugin {
try { try {
previousVersion.save(versionFile); previousVersion.save(versionFile);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); getLogger().log(java.util.logging.Level.SEVERE, "Unable to save new database version " + Migrations.CURRENT_DATABASE_VERSION, e);
} }
} }
@ -255,7 +276,7 @@ public class ChestShop extends JavaPlugin {
try { try {
previousVersion.save(versionFile); previousVersion.save(versionFile);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); getLogger().log(java.util.logging.Level.SEVERE, "Unable to save new database version " + newVersion, e);
} }
} }
return true; return true;
@ -276,7 +297,7 @@ public class ChestShop extends JavaPlugin {
file.createNewFile(); file.createNewFile();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); getBukkitLogger().log(java.util.logging.Level.SEVERE, "Unable to load file " + file.getName(), e);
} }
} }
@ -289,14 +310,17 @@ public class ChestShop extends JavaPlugin {
try { try {
handler = new FileHandler(path, true); handler = new FileHandler(path, true);
} catch (IOException ex) { } catch (IOException ex) {
ex.printStackTrace(); getBukkitLogger().log(java.util.logging.Level.SEVERE, "Unable to load handler " + path, ex);
} }
return handler; return handler;
} }
public void onDisable() { public void onDisable() {
getServer().getScheduler().cancelTasks(this); executorService.shutdown();
try {
executorService.awaitTermination(15, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {}
Toggle.clearToggledPlayers(); Toggle.clearToggledPlayers();
@ -430,16 +454,21 @@ public class ChestShop extends JavaPlugin {
} }
private void startStatistics() { private void startStatistics() {
Metrics bStats = new Metrics(this, 1109); try (JarFile jarFile = new JarFile(this.getFile())) {
try { String dist = jarFile.getManifest().getMainAttributes().getValue("Distribution-Type");
String dist = new JarFile(this.getFile()).getManifest().getMainAttributes().getValue("Distribution-Type"); bStats.addCustomChart(new SimplePie("distributionType", () -> dist));
bStats.addCustomChart(new Metrics.SimplePie("distributionType", () -> dist));
} catch (IOException ignored) {} } catch (IOException ignored) {}
String serverVersion = getServer().getBukkitVersion().split("-")[0]; String serverVersion = getServer().getBukkitVersion().split("-")[0];
bStats.addCustomChart(createStaticDrilldownStat("versionMcSelf", serverVersion, getDescription().getVersion())); bStats.addCustomChart(createStaticDrilldownStat("versionMcSelf", serverVersion, getDescription().getVersion()));
bStats.addCustomChart(createStaticDrilldownStat("versionSelfMc", getDescription().getVersion(), serverVersion)); 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"); String javaVersion = System.getProperty("java.version");
bStats.addCustomChart(createStaticDrilldownStat("versionJavaSelf", javaVersion, getDescription().getVersion())); bStats.addCustomChart(createStaticDrilldownStat("versionJavaSelf", javaVersion, getDescription().getVersion()));
bStats.addCustomChart(createStaticDrilldownStat("versionSelfJava", getDescription().getVersion(), javaVersion)); bStats.addCustomChart(createStaticDrilldownStat("versionSelfJava", getDescription().getVersion(), javaVersion));
@ -447,22 +476,36 @@ public class ChestShop extends JavaPlugin {
bStats.addCustomChart(createStaticDrilldownStat("versionJavaMc", javaVersion, serverVersion)); bStats.addCustomChart(createStaticDrilldownStat("versionJavaMc", javaVersion, serverVersion));
bStats.addCustomChart(createStaticDrilldownStat("versionMcJava", serverVersion, javaVersion)); bStats.addCustomChart(createStaticDrilldownStat("versionMcJava", serverVersion, javaVersion));
bStats.addCustomChart(new Metrics.SingleLineChart("shopAccounts", NameManager::getAccountCount)); bStats.addCustomChart(new SingleLineChart("shopAccounts", NameManager::getAccountCount));
bStats.addCustomChart(new Metrics.MultiLineChart("transactionCount", () -> ImmutableMap.of( bStats.addCustomChart(new MultiLineChart("transactionCount", () -> ImmutableMap.of(
"total", MetricsModule.getTotalTransactions(), "total", MetricsModule.getTotalTransactions(),
"buy", MetricsModule.getBuyTransactions(), "buy", MetricsModule.getBuyTransactions(),
"sell", MetricsModule.getSellTransactions() "sell", MetricsModule.getSellTransactions()
))); )));
bStats.addCustomChart(new Metrics.MultiLineChart("itemCount", () -> ImmutableMap.of( bStats.addCustomChart(new MultiLineChart("itemCount", () -> ImmutableMap.of(
"total", MetricsModule.getTotalItemsCount(), "total", MetricsModule.getTotalItemsCount(),
"buy", MetricsModule.getSoldItemsCount(), "buy", MetricsModule.getSoldItemsCount(),
"sell", MetricsModule.getBoughtItemsCount() "sell", MetricsModule.getBoughtItemsCount()
))); )));
bStats.addCustomChart(new Metrics.SimplePie("includeSettingsInMetrics", () -> Properties.INCLUDE_SETTINGS_IN_METRICS ? "enabled" : "disabled")); bStats.addCustomChart(new SimplePie("includeSettingsInMetrics", () -> Properties.INCLUDE_SETTINGS_IN_METRICS ? "enabled" : "disabled"));
if (!Properties.INCLUDE_SETTINGS_IN_METRICS) return; if (!Properties.INCLUDE_SETTINGS_IN_METRICS) return;
bStats.addCustomChart(new Metrics.AdvancedBarChart("pluginProperties", () -> { 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<String, int[]> map = new LinkedHashMap<>();
map.put("ensure-correct-playerid", getChartArray(Properties.ENSURE_CORRECT_PLAYERID)); map.put("ensure-correct-playerid", getChartArray(Properties.ENSURE_CORRECT_PLAYERID));
map.put("reverse-buttons", getChartArray(Properties.REVERSE_BUTTONS)); map.put("reverse-buttons", getChartArray(Properties.REVERSE_BUTTONS));
@ -479,15 +522,18 @@ public class ChestShop extends JavaPlugin {
map.put("bungeecord-messages", getChartArray(Properties.BUNGEECORD_MESSAGES)); map.put("bungeecord-messages", getChartArray(Properties.BUNGEECORD_MESSAGES));
map.put("log-to-console", getChartArray(Properties.LOG_TO_CONSOLE)); map.put("log-to-console", getChartArray(Properties.LOG_TO_CONSOLE));
map.put("log-to-file", getChartArray(Properties.LOG_TO_FILE)); 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; return map;
})); }));
bStats.addCustomChart(new Metrics.SimpleBarChart("shopContainers", bStats.addCustomChart(new SimpleBarChart("shopContainers",
() -> Properties.SHOP_CONTAINERS.stream().map(Material::name).collect(Collectors.toMap(k -> k, k -> 1)))); () -> Properties.SHOP_CONTAINERS.stream().map(Material::name).collect(Collectors.toMap(k -> k, k -> 1))));
} }
private Metrics.DrilldownPie createStaticDrilldownStat(String statId, String value1, String value2) { public static DrilldownPie createStaticDrilldownStat(String statId, String value1, String value2) {
final Map<String, Map<String, Integer>> map = ImmutableMap.of(value1, ImmutableMap.of(value2, 1)); final Map<String, Map<String, Integer>> map = ImmutableMap.of(value1, ImmutableMap.of(value2, 1));
return new Metrics.DrilldownPie(statId, () -> map); return new DrilldownPie(statId, () -> map);
} }
private int[] getChartArray(boolean value) { private int[] getChartArray(boolean value) {
@ -499,10 +545,18 @@ public class ChestShop extends JavaPlugin {
private void startUpdater() { private void startUpdater() {
if (Properties.TURN_OFF_UPDATES) { 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!"); 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; 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 static final String PROJECT_JENKINS_JOB_URL = "https://ci.minebench.de/job/ChestShop-3/";
@ -525,10 +579,20 @@ public class ChestShop extends JavaPlugin {
return dataFolder; return dataFolder;
} }
public static Logger getShopLogger() {
return shopLogger;
}
public static Logger getBukkitLogger() { public static Logger getBukkitLogger() {
return logger; return logger;
} }
public static void logDebug(String message) {
if (Properties.DEBUG) {
getBukkitLogger().info("[DEBUG] " + message);
}
}
public static Server getBukkitServer() { public static Server getBukkitServer() {
return server; return server;
} }
@ -549,6 +613,10 @@ public class ChestShop extends JavaPlugin {
return plugin; return plugin;
} }
public static Metrics getMetrics() {
return bStats;
}
public static BukkitAudiences getAudiences() { public static BukkitAudiences getAudiences() {
return audiences; return audiences;
} }
@ -588,4 +656,8 @@ public class ChestShop extends JavaPlugin {
Bukkit.getOnlinePlayers().iterator().next().sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); Bukkit.getOnlinePlayers().iterator().next().sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
} }
} }
public static void runInAsyncThread(Runnable runnable) {
executorService.submit(runnable);
}
} }

View File

@ -1,7 +1,7 @@
package com.Acrobot.ChestShop.Commands; package com.Acrobot.ChestShop.Commands;
import com.Acrobot.ChestShop.Configuration.Messages; import com.Acrobot.ChestShop.Configuration.Messages;
import org.apache.commons.lang.Validate; import com.google.common.base.Preconditions;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
@ -43,7 +43,7 @@ public class AccessToggle implements CommandExecutor {
} }
public static boolean setIgnoring(Player player, boolean ignoring) { 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 (ignoring) {
toggledPlayers.add(player.getUniqueId()); toggledPlayers.add(player.getUniqueId());

View File

@ -48,17 +48,7 @@ public class ItemInfo implements CommandExecutor {
} }
iteminfo.send(sender); iteminfo.send(sender);
try { if (!sendItemName(sender, item, Messages.iteminfo_fullname)) return true;
Map<String, String> replacementMap = ImmutableMap.of("item", ItemUtil.getName(item));
if (!Properties.SHOWITEM_MESSAGE || !(sender instanceof Player)
|| !MaterialUtil.Show.sendMessage((Player) sender, sender.getName(), Messages.iteminfo_fullname, false, new ItemStack[]{item}, replacementMap)) {
Messages.iteminfo_fullname.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 true;
}
try { try {
iteminfo_shopname.send(sender, "item", ItemUtil.getSignName(item)); iteminfo_shopname.send(sender, "item", ItemUtil.getSignName(item));
@ -73,4 +63,19 @@ public class ItemInfo implements CommandExecutor {
return true; return true;
} }
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;
}
return true;
}
} }

View File

@ -1,7 +1,7 @@
package com.Acrobot.ChestShop.Commands; package com.Acrobot.ChestShop.Commands;
import com.Acrobot.ChestShop.Configuration.Messages; import com.Acrobot.ChestShop.Configuration.Messages;
import org.apache.commons.lang.Validate; import com.google.common.base.Preconditions;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@ -61,7 +61,7 @@ public class Toggle implements CommandExecutor {
} }
public static boolean setIgnoring(Player player, boolean ignoring) { 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 (ignoring) {
toggledPlayers.add(player.getUniqueId()); toggledPlayers.add(player.getUniqueId());

View File

@ -34,6 +34,15 @@ public class Messages {
public static Message iteminfo_repaircost; public static Message iteminfo_repaircost;
public static Message iteminfo_book; public static Message iteminfo_book;
public static Message iteminfo_book_generation; 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; public static Message iteminfo_lore;
public static Message METRICS; public static Message METRICS;
@ -73,6 +82,7 @@ public class Messages {
public static Message INVALID_SHOP_PRICE; public static Message INVALID_SHOP_PRICE;
public static Message INVALID_SHOP_QUANTITY; public static Message INVALID_SHOP_QUANTITY;
public static Message CANNOT_ACCESS_THE_CHEST; public static Message CANNOT_ACCESS_THE_CHEST;
public static Message CANNOT_CHANGE_SIGN_BACKSIDE;
public static Message SELL_PRICE_HIGHER_THAN_BUY_PRICE; public static Message SELL_PRICE_HIGHER_THAN_BUY_PRICE;
public static Message SELL_PRICE_ABOVE_MAX; public static Message SELL_PRICE_ABOVE_MAX;

View File

@ -103,10 +103,13 @@ public class Properties {
@ConfigurationComment("Do you want to turn off the automatic updates of ChestShop?") @ConfigurationComment("Do you want to turn off the automatic updates of ChestShop?")
public static boolean TURN_OFF_UPDATES = true; 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?") @ConfigurationComment("Do you want to turn off the automatic notifications for new development builds?")
public static boolean TURN_OFF_DEV_UPDATE_NOTIFIER = false; 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 leak sensitive data but help in the development process)") @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; public static boolean INCLUDE_SETTINGS_IN_METRICS = true;
@PrecededBySpace @PrecededBySpace
@ -120,6 +123,9 @@ public class Properties {
@ConfigurationComment("Should the plugin try to use a language file that matches the client's locale setting?") @ConfigurationComment("Should the plugin try to use a language file that matches the client's locale setting?")
public static boolean USE_CLIENT_LOCALE = true; 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 @PrecededBySpace
@ConfigurationComment("What containers are allowed to hold a shop? (Only blocks with inventories work!)") @ConfigurationComment("What containers are allowed to hold a shop? (Only blocks with inventories work!)")
@Parser("MaterialSet") @Parser("MaterialSet")
@ -184,10 +190,10 @@ public class Properties {
public static UUID SERVER_ECONOMY_ACCOUNT_UUID = new UUID(0, 0); 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)") @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.") @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") @ConfigurationComment("Amount of money player must pay to create a shop")
public static BigDecimal SHOP_CREATION_PRICE = BigDecimal.valueOf(0); public static BigDecimal SHOP_CREATION_PRICE = BigDecimal.valueOf(0);
@ -202,8 +208,8 @@ public class Properties {
public static boolean ENSURE_CORRECT_PLAYERID = true; 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" + @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" + "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 *, change the first * in the regexp (the one before the question mark) to whatever your prefix is.") "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+$"; public static String VALID_PLAYERNAME_REGEXP = "^\\w+$";
@PrecededBySpace @PrecededBySpace
@ -252,10 +258,10 @@ public class Properties {
@ConfigurationComment("If true, plugin will log transactions in its own file") @ConfigurationComment("If true, plugin will log transactions in its own file")
public static boolean LOG_TO_FILE = false; 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; public static boolean LOG_TO_CONSOLE = true;
@ConfigurationComment("Should all shop removals be logged to the console?") @ConfigurationComment("Should all shop removals be logged?")
public static boolean LOG_ALL_SHOP_REMOVALS = true; public static boolean LOG_ALL_SHOP_REMOVALS = true;
@PrecededBySpace @PrecededBySpace

View File

@ -48,7 +48,7 @@ public class AdminInventory implements Inventory {
@Override @Override
public ItemStack getItem(int i) { public ItemStack getItem(int i) {
if (content.length < i) { if (content.length > i) {
return content[i]; return content[i];
} }
return null; return null;
@ -156,10 +156,12 @@ public class AdminInventory implements Inventory {
public HashMap<Integer, ? extends ItemStack> all(ItemStack itemStack) { public HashMap<Integer, ? extends ItemStack> all(ItemStack itemStack) {
HashMap<Integer, ItemStack> items = new HashMap<Integer, ItemStack>(); HashMap<Integer, ItemStack> items = new HashMap<Integer, ItemStack>();
ItemStack clone = itemStack.clone(); if (itemStack != null) {
clone.setAmount(Integer.MAX_VALUE); ItemStack clone = itemStack.clone();
clone.setAmount(Integer.MAX_VALUE);
items.put(1, clone); items.put(1, clone);
}
return items; return items;
} }

View File

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

View File

@ -28,8 +28,6 @@ public class Migrations {
public static int migrate(int currentVersion) { public static int migrate(int currentVersion) {
if (currentVersion != CURRENT_DATABASE_VERSION) { if (currentVersion != CURRENT_DATABASE_VERSION) {
ChestShop.getBukkitLogger().info("Updating database..."); ChestShop.getBukkitLogger().info("Updating database...");
} else {
return CURRENT_DATABASE_VERSION;
} }
switch (currentVersion) { switch (currentVersion) {
@ -67,7 +65,7 @@ public class Migrations {
accounts.executeRaw("ALTER TABLE `accounts` ADD COLUMN lastSeenName VARCHAR"); accounts.executeRaw("ALTER TABLE `accounts` ADD COLUMN lastSeenName VARCHAR");
return true; return true;
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while migrating database to v2", e);
return false; return false;
} }
} }
@ -113,7 +111,7 @@ public class Migrations {
} }
try { try {
results.close(); results.close();
} catch (IOException e1) { } catch (Exception e1) {
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while closing results! " + e.getMessage()); 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, success + " accounts successfully migrated. " + error + " accounts failed to migrate!");
@ -122,7 +120,7 @@ public class Migrations {
return true; return true;
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while migrating database to v3", e);
return false; return false;
} }
} }
@ -139,13 +137,13 @@ public class Migrations {
try { try {
items.executeRawNoArgs("INSERT INTO `items` (id, code) SELECT id, code uuid FROM `items-old`"); items.executeRawNoArgs("INSERT INTO `items` (id, code) SELECT id, code uuid FROM `items-old`");
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); 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!"); ChestShop.getBukkitLogger().log(Level.INFO, "Migration of items table finished in " + (System.currentTimeMillis() - start) / 1000.0 + "s!");
return true; return true;
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while migrating database to v4", e);
return false; return false;
} }
} }

View File

@ -2,9 +2,12 @@ package com.Acrobot.ChestShop;
import com.Acrobot.Breeze.Utils.MaterialUtil; import com.Acrobot.Breeze.Utils.MaterialUtil;
import com.Acrobot.ChestShop.Configuration.Properties; 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.ReserveListener;
import com.Acrobot.ChestShop.Listeners.Economy.Plugins.VaultListener; import com.Acrobot.ChestShop.Listeners.Economy.Plugins.VaultListener;
import com.Acrobot.ChestShop.Plugins.*; import com.Acrobot.ChestShop.Plugins.*;
import com.google.common.collect.ImmutableMap;
import org.bstats.charts.DrilldownPie;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
@ -14,11 +17,19 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager; 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 * @author Acrobot
*/ */
public class Dependencies implements Listener { public class Dependencies implements Listener {
private static final Map<String, String> versions = new HashMap<>();
public static void initializePlugins() { public static void initializePlugins() {
PluginManager pluginManager = Bukkit.getPluginManager(); PluginManager pluginManager = Bukkit.getPluginManager();
@ -58,17 +69,31 @@ public class Dependencies implements Listener {
Plugin plugin = pluginManager.getPlugin(dependency); Plugin plugin = pluginManager.getPlugin(dependency);
if (plugin != null && plugin.isEnabled()) { if (plugin != null && plugin.isEnabled()) {
loadPlugin(dependency, plugin); try {
loadPlugin(dependency, plugin);
} catch (Exception e) {
plugin.getLogger().log(Level.WARNING, "Unable to hook into " + plugin.getName() + " " + plugin.getDescription().getVersion(), e);
}
} }
} }
return loadEconomy(); 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() { private static boolean loadEconomy() {
String plugin = "none"; String plugin = "none";
Listener economy = null; EconomyAdapter economy = null;
if(Bukkit.getPluginManager().getPlugin("Reserve") != null) { if(Bukkit.getPluginManager().getPlugin("Reserve") != null) {
plugin = "Reserve"; plugin = "Reserve";
@ -85,6 +110,9 @@ public class Dependencies implements Listener {
return false; 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.registerListener(economy);
ChestShop.getBukkitLogger().info(plugin + " loaded!"); ChestShop.getBukkitLogger().info(plugin + " loaded!");
return true; return true;
@ -174,6 +202,9 @@ public class Dependencies implements Listener {
listener = heroes; listener = heroes;
break; break;
case ItemBridge:
listener = new ItemBridge();
break;
case ShowItem: case ShowItem:
MaterialUtil.Show.initialize(plugin); MaterialUtil.Show.initialize(plugin);
break; break;
@ -184,6 +215,7 @@ public class Dependencies implements Listener {
} }
PluginDescriptionFile description = plugin.getDescription(); PluginDescriptionFile description = plugin.getDescription();
versions.put(description.getName(), description.getVersion());
ChestShop.getBukkitLogger().info(description.getName() + " version " + description.getVersion() + " loaded."); ChestShop.getBukkitLogger().info(description.getName() + " version " + description.getVersion() + " loaded.");
} }
@ -202,6 +234,8 @@ public class Dependencies implements Listener {
Heroes, Heroes,
ItemBridge,
ShowItem ShowItem
} }

View File

@ -99,6 +99,13 @@ public class CurrencyAddEvent extends EconomicEvent {
return target; return target;
} }
/**
* @param target Account from which the currency is subtracted
*/
public void setTarget(UUID target) {
this.target = target;
}
public HandlerList getHandlers() { public HandlerList getHandlers() {
return handlers; return handlers;
} }

View File

@ -76,6 +76,13 @@ public class CurrencyAmountEvent extends EconomicEvent {
return account; return account;
} }
/**
* @param account Account that is checked
*/
public void setAccount(UUID account) {
this.account = account;
}
public HandlerList getHandlers() { public HandlerList getHandlers() {
return handlers; return handlers;
} }

View File

@ -103,9 +103,7 @@ public class CurrencyCheckEvent extends EconomicEvent {
* Sets the account name * Sets the account name
* *
* @param account Account name * @param account Account name
* @deprecated The account should not be changed!
*/ */
@Deprecated
public void setAccount(UUID account) { public void setAccount(UUID account) {
this.account = account; this.account = account;
} }

View File

@ -2,7 +2,6 @@ package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -100,6 +99,13 @@ public class CurrencySubtractEvent extends EconomicEvent {
return target; return target;
} }
/**
* @param target Account from which the currency is subtracted
*/
public void setTarget(UUID target) {
this.target = target;
}
public HandlerList getHandlers() { public HandlerList getHandlers() {
return handlers; return handlers;
} }

View File

@ -1,8 +1,8 @@
package com.Acrobot.ChestShop.Events.Economy; package com.Acrobot.ChestShop.Events.Economy;
import com.Acrobot.ChestShop.Events.TransactionEvent;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -25,26 +25,32 @@ public class CurrencyTransferEvent extends EconomicEvent {
private Direction direction; private Direction direction;
private final TransactionEvent transactionEvent;
public CurrencyTransferEvent(BigDecimal amount, Player initiator, UUID partner, Direction direction) { public CurrencyTransferEvent(BigDecimal amount, Player initiator, UUID partner, Direction direction) {
this(amount, amount, initiator, partner, direction); this(amount, amount, initiator, partner, direction);
} }
public CurrencyTransferEvent(BigDecimal amountSent, BigDecimal amountReceived, Player initiator, UUID partner, Direction direction) { 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.amountSent = amountSent;
this.amountReceived = amountReceived; this.amountReceived = amountReceived;
this.initiator = initiator; this.initiator = initiator;
this.partner = partner; this.partner = partner;
this.direction = direction; this.direction = direction;
this.transactionEvent = transactionEvent;
} }
/**
* @deprecated Use {{@link #CurrencyTransferEvent(BigDecimal, Player, UUID, Direction)}
*/
@Deprecated
public CurrencyTransferEvent(double amount, Player initiator, UUID partner, Direction direction) {
this(BigDecimal.valueOf(amount), initiator, partner, direction);
}
/** /**
* @return Amount of currency sent * @return Amount of currency sent
@ -150,6 +156,15 @@ public class CurrencyTransferEvent extends EconomicEvent {
return direction; 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 * Get the player who initiated this transaction
* *
@ -166,6 +181,15 @@ public class CurrencyTransferEvent extends EconomicEvent {
return partner; 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 * @return The world in which the transaction occurs
*/ */

View File

@ -9,13 +9,24 @@ public class ItemStringQueryEvent extends Event {
private String itemString = null; private String itemString = null;
private final ItemStack item; private final ItemStack item;
private final int maxWidth;
/** /**
* Query the item string representation of a certain item with a certain length * Query the item string representation of a certain item with a certain length
* @param item The item to query the string for * @param item The item to query the string for
*/ */
public ItemStringQueryEvent(ItemStack item) { 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.item = item;
this.maxWidth = maxWidth;
} }
@Override @Override
@ -50,4 +61,12 @@ public class ItemStringQueryEvent extends Event {
public void setItemString(String itemString) { public void setItemString(String itemString) {
this.itemString = 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

@ -66,10 +66,6 @@ public class BlockPlace implements Listener {
public static void onHopperDropperPlace(BlockPlaceEvent event) { public static void onHopperDropperPlace(BlockPlaceEvent event) {
Block placed = event.getBlockPlaced(); Block placed = event.getBlockPlaced();
if (placed.getType() != Material.HOPPER && placed.getType() != Material.DROPPER) {
return;
}
List<BlockFace> searchDirections = new ArrayList<>(); List<BlockFace> searchDirections = new ArrayList<>();
switch (placed.getType()) { switch (placed.getType()) {
case HOPPER: case HOPPER:
@ -79,6 +75,8 @@ public class BlockPlace implements Listener {
case DROPPER: case DROPPER:
searchDirections.add(((Directional) placed.getBlockData()).getFacing()); searchDirections.add(((Directional) placed.getBlockData()).getFacing());
break; break;
default:
return;
} }
for (BlockFace face : searchDirections) { for (BlockFace face : searchDirections) {

View File

@ -24,6 +24,7 @@ import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.FixedMetadataValue;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
@ -44,9 +45,9 @@ public class SignBreak implements Listener {
public SignBreak() { public SignBreak() {
try { try {
Class.forName("com.destroystokyo.paper.event.block.BlockDestroyEvent"); Class.forName("com.destroystokyo.paper.event.block.BlockDestroyEvent");
ChestShop.getPlugin().registerEvent((Listener) Class.forName("com.Acrobot.ChestShop.Listeners.Block.Break.Attached.PaperBlockDestroy").newInstance()); ChestShop.getPlugin().registerEvent((Listener) Class.forName("com.Acrobot.ChestShop.Listeners.Block.Break.Attached.PaperBlockDestroy").getDeclaredConstructor().newInstance());
ChestShop.getBukkitLogger().info("Using Paper's BlockDestroyEvent instead of the BlockPhysicsEvent!"); ChestShop.getBukkitLogger().info("Using Paper's BlockDestroyEvent instead of the BlockPhysicsEvent!");
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
ChestShop.getPlugin().registerEvent(new PhysicsBreak()); ChestShop.getPlugin().registerEvent(new PhysicsBreak());
} }
} }

View File

@ -3,13 +3,16 @@ package com.Acrobot.ChestShop.Listeners.Block;
import com.Acrobot.Breeze.Utils.BlockUtil; import com.Acrobot.Breeze.Utils.BlockUtil;
import com.Acrobot.Breeze.Utils.StringUtil; import com.Acrobot.Breeze.Utils.StringUtil;
import com.Acrobot.ChestShop.ChestShop; import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Events.PreShopCreationEvent; import com.Acrobot.ChestShop.Events.PreShopCreationEvent;
import com.Acrobot.ChestShop.Events.ShopCreatedEvent; import com.Acrobot.ChestShop.Events.ShopCreatedEvent;
import com.Acrobot.ChestShop.Listeners.Block.Break.SignBreak;
import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.Acrobot.ChestShop.Signs.ChestShopSign;
import com.Acrobot.ChestShop.UUIDs.NameManager; import com.Acrobot.ChestShop.UUIDs.NameManager;
import com.Acrobot.ChestShop.Utils.uBlock; import com.Acrobot.ChestShop.Utils.uBlock;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.block.SignChangeEvent;
@ -21,6 +24,17 @@ import static com.Acrobot.ChestShop.Permission.OTHER_NAME_DESTROY;
*/ */
public class SignCreate implements Listener { public class SignCreate implements Listener {
private static boolean HAS_SIGN_SIDES;
static {
try {
SignChangeEvent.class.getMethod("getSide");
HAS_SIGN_SIDES = true;
} catch (NoSuchMethodException e) {
HAS_SIGN_SIDES = false;
}
}
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public static void onSignChange(SignChangeEvent event) { public static void onSignChange(SignChangeEvent event) {
Block signBlock = event.getBlock(); Block signBlock = event.getBlock();
@ -31,15 +45,28 @@ public class SignCreate implements Listener {
Sign sign = (Sign) signBlock.getState(); Sign sign = (Sign) signBlock.getState();
if (HAS_SIGN_SIDES && event.getSide() != Side.FRONT) {
if (ChestShopSign.isValid(sign)) {
event.setCancelled(true);
Messages.CANNOT_CHANGE_SIGN_BACKSIDE.sendWithPrefix(event.getPlayer());
}
return;
}
if (ChestShopSign.isValid(event.getLines()) && !NameManager.canUseName(event.getPlayer(), OTHER_NAME_DESTROY, ChestShopSign.getOwner(event.getLines()))) { if (ChestShopSign.isValid(event.getLines()) && !NameManager.canUseName(event.getPlayer(), OTHER_NAME_DESTROY, ChestShopSign.getOwner(event.getLines()))) {
event.setCancelled(true); event.setCancelled(true);
sign.update(); sign.update();
ChestShop.logDebug("Shop sign creation at " + sign.getLocation() + " by " + event.getPlayer().getName() + " was cancelled as they weren't able to create a shop for the account '" + ChestShopSign.getOwner(event.getLines()) + "'");
return; return;
} }
String[] lines = StringUtil.stripColourCodes(event.getLines()); String[] lines = StringUtil.stripColourCodes(event.getLines());
if (!ChestShopSign.isValidPreparedSign(lines)) { if (!ChestShopSign.isValidPreparedSign(lines)) {
// Check if a valid shop already existed previously
if (ChestShopSign.isValid(sign)) {
SignBreak.sendShopDestroyedEvent(sign, event.getPlayer());
}
return; return;
} }
@ -49,6 +76,7 @@ public class SignCreate implements Listener {
if (preEvent.getOutcome().shouldBreakSign()) { if (preEvent.getOutcome().shouldBreakSign()) {
event.setCancelled(true); event.setCancelled(true);
signBlock.breakNaturally(); signBlock.breakNaturally();
ChestShop.logDebug("Shop sign creation at " + sign.getLocation() + " by " + event.getPlayer().getName() + " was cancelled (creation outcome: " + preEvent.getOutcome() + ") and the sign broken");
return; return;
} }
@ -57,6 +85,7 @@ public class SignCreate implements Listener {
} }
if (preEvent.isCancelled()) { if (preEvent.isCancelled()) {
ChestShop.logDebug("Shop sign creation at " + sign.getLocation() + " by " + event.getPlayer().getName() + " was cancelled (creation outcome: " + preEvent.getOutcome() + ") and sign lines were set to " + String.join(", ", preEvent.getSignLines()));
return; return;
} }

View File

@ -16,6 +16,8 @@ import java.math.BigDecimal;
public abstract class EconomyAdapter implements Listener { public abstract class EconomyAdapter implements Listener {
public abstract ProviderInfo getProviderInfo();
public abstract void onAmountCheck(CurrencyAmountEvent event); public abstract void onAmountCheck(CurrencyAmountEvent event);
public abstract void onCurrencyCheck(CurrencyCheckEvent event); public abstract void onCurrencyCheck(CurrencyCheckEvent event);
@ -74,4 +76,21 @@ public abstract class EconomyAdapter implements Listener {
} }
} }
public static class ProviderInfo {
private final String name;
private final String version;
public ProviderInfo(String name, String version) {
this.name = name;
this.version = version;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
}
} }

View File

@ -1,5 +1,6 @@
package com.Acrobot.ChestShop.Listeners.Economy.Plugins; package com.Acrobot.ChestShop.Listeners.Economy.Plugins;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Events.Economy.AccountCheckEvent; import com.Acrobot.ChestShop.Events.Economy.AccountCheckEvent;
import com.Acrobot.ChestShop.Events.Economy.CurrencyAddEvent; import com.Acrobot.ChestShop.Events.Economy.CurrencyAddEvent;
import com.Acrobot.ChestShop.Events.Economy.CurrencyAmountEvent; import com.Acrobot.ChestShop.Events.Economy.CurrencyAmountEvent;
@ -12,6 +13,7 @@ import com.Acrobot.ChestShop.Listeners.Economy.EconomyAdapter;
import net.tnemc.core.Reserve; import net.tnemc.core.Reserve;
import net.tnemc.core.economy.EconomyAPI; import net.tnemc.core.economy.EconomyAPI;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -26,10 +28,15 @@ public class ReserveListener extends EconomyAdapter {
private static @Nullable EconomyAPI economyAPI; private static @Nullable EconomyAPI economyAPI;
public ReserveListener(EconomyAPI api) { public ReserveListener(@Nullable EconomyAPI api) {
ReserveListener.economyAPI = api; ReserveListener.economyAPI = api;
} }
@Override
public ProviderInfo getProviderInfo() {
return new ProviderInfo(economyAPI.name(), economyAPI.version());
}
public static EconomyAPI getProvider() { public static EconomyAPI getProvider() {
return economyAPI; return economyAPI;
} }
@ -99,7 +106,8 @@ public class ReserveListener extends EconomyAdapter {
} }
if (provided()) { if (provided()) {
event.setFormattedAmount(economyAPI.format(event.getAmount())); String formatted = economyAPI.format(event.getAmount());
event.setFormattedAmount(Properties.STRIP_PRICE_COLORS ? ChatColor.stripColor(formatted) : formatted);
event.setHandled(true); event.setHandled(true);
} }
} }
@ -127,7 +135,7 @@ public class ReserveListener extends EconomyAdapter {
@EventHandler @EventHandler
public void onCurrencyHoldCheck(CurrencyHoldEvent event) { public void onCurrencyHoldCheck(CurrencyHoldEvent event) {
if (event.getAccount() == null || event.wasHandled() || !transactionCanFail() || event.canHold()) { if (!provided() || event.getAccount() == null || event.wasHandled() || !transactionCanFail() || event.canHold()) {
return; return;
} }

View File

@ -5,16 +5,19 @@ import java.util.logging.Level;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Listeners.Economy.EconomyAdapter; import com.Acrobot.ChestShop.Listeners.Economy.EconomyAdapter;
import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse; import net.milkbowl.vault.economy.EconomyResponse;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.server.ServiceRegisterEvent; import org.bukkit.event.server.ServiceRegisterEvent;
import org.bukkit.event.server.ServiceUnregisterEvent; import org.bukkit.event.server.ServiceUnregisterEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
import com.Acrobot.ChestShop.ChestShop; import com.Acrobot.ChestShop.ChestShop;
@ -35,6 +38,7 @@ import com.Acrobot.ChestShop.Events.Economy.CurrencyTransferEvent;
public class VaultListener extends EconomyAdapter { public class VaultListener extends EconomyAdapter {
private RegisteredServiceProvider<Economy> rsp; private RegisteredServiceProvider<Economy> rsp;
private static Economy provider; private static Economy provider;
private Plugin providingPlugin;
private VaultListener() { private VaultListener() {
updateEconomyProvider(); updateEconomyProvider();
@ -45,6 +49,7 @@ public class VaultListener extends EconomyAdapter {
if (rsp != null) { if (rsp != null) {
provider = rsp.getProvider(); provider = rsp.getProvider();
providingPlugin = rsp.getPlugin();
ChestShop.getBukkitLogger().log(Level.INFO, "Using " + provider.getName() + " as the Economy provider now."); ChestShop.getBukkitLogger().log(Level.INFO, "Using " + provider.getName() + " as the Economy provider now.");
} }
} }
@ -58,6 +63,11 @@ public class VaultListener extends EconomyAdapter {
return true; return true;
} }
@Override
public ProviderInfo getProviderInfo() {
return new ProviderInfo(provider.getName(), providingPlugin.getDescription().getVersion());
}
public static Economy getProvider() { return provider; } public static Economy getProvider() { return provider; }
public boolean transactionCanFail() { public boolean transactionCanFail() {
@ -172,7 +182,7 @@ public class VaultListener extends EconomyAdapter {
} }
String formatted = provider.format(event.getAmount().doubleValue()); String formatted = provider.format(event.getAmount().doubleValue());
event.setFormattedAmount(formatted); event.setFormattedAmount(Properties.STRIP_PRICE_COLORS ? ChatColor.stripColor(formatted) : formatted);
event.setHandled(true); event.setHandled(true);
} }

View File

@ -1,6 +1,5 @@
package com.Acrobot.ChestShop.Listeners.Economy; package com.Acrobot.ChestShop.Listeners.Economy;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Database.Account; import com.Acrobot.ChestShop.Database.Account;
import com.Acrobot.ChestShop.Events.Economy.*; import com.Acrobot.ChestShop.Events.Economy.*;
import com.Acrobot.ChestShop.UUIDs.NameManager; import com.Acrobot.ChestShop.UUIDs.NameManager;
@ -27,15 +26,12 @@ public class ServerAccountCorrector implements Listener {
Account account = NameManager.getServerEconomyAccount(); Account account = NameManager.getServerEconomyAccount();
target = account != null ? account.getUuid() : null; target = account != null ? account.getUuid() : null;
event.setHandled(true);
if (target == null) { if (target == null) {
event.setHandled(true);
return; return;
} }
CurrencyAddEvent currencyAddEvent = new CurrencyAddEvent(event.getAmount(), target, event.getWorld()); event.setTarget(target);
ChestShop.callEvent(currencyAddEvent);
event.setHandled(currencyAddEvent.wasHandled());
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
@ -49,15 +45,12 @@ public class ServerAccountCorrector implements Listener {
Account account = NameManager.getServerEconomyAccount(); Account account = NameManager.getServerEconomyAccount();
target = account != null ? account.getUuid() : null; target = account != null ? account.getUuid() : null;
event.setHandled(true);
if (target == null) { if (target == null) {
event.setHandled(true);
return; return;
} }
CurrencySubtractEvent currencySubtractEvent = new CurrencySubtractEvent(event.getAmount(), target, event.getWorld()); event.setTarget(target);
ChestShop.callEvent(currencySubtractEvent);
event.setHandled(currencySubtractEvent.wasHandled());
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
@ -75,15 +68,7 @@ public class ServerAccountCorrector implements Listener {
return; return;
} }
CurrencyTransferEvent currencyTransferEvent = new CurrencyTransferEvent( event.setPartner(partner);
event.getAmountSent(),
event.getAmountReceived(),
event.getInitiator(),
partner,
event.getDirection()
);
ChestShop.callEvent(currencyTransferEvent);
event.setHandled(currencyTransferEvent.wasHandled());
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
@ -102,10 +87,7 @@ public class ServerAccountCorrector implements Listener {
return; return;
} }
CurrencyCheckEvent currencyCheckEvent = new CurrencyCheckEvent(event.getAmount(), target, event.getWorld()); event.setAccount(target);
ChestShop.callEvent(currencyCheckEvent);
event.hasEnough(currencyCheckEvent.hasEnough());
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
@ -116,8 +98,15 @@ public class ServerAccountCorrector implements Listener {
return; return;
} }
event.canHold(true); Account account = NameManager.getServerEconomyAccount();
event.setAccount(null); target = account != null ? account.getUuid() : null;
if (target == null) {
event.canHold(true);
return;
}
event.setAccount(target);
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
@ -136,9 +125,6 @@ public class ServerAccountCorrector implements Listener {
return; return;
} }
CurrencyAmountEvent currencyAmountEvent = new CurrencyAmountEvent(target, event.getWorld()); event.setAccount(target);
ChestShop.callEvent(currencyAmountEvent);
event.setAmount(currencyAmountEvent.getAmount());
} }
} }

View File

@ -20,8 +20,8 @@ public class TaxModule implements Listener {
private static final String TAX_RECEIVED_MESSAGE = "Applied a tax of %1$f percent (%2$.2f) to the received amount for a resulting price of %3$.2f"; private static final String TAX_RECEIVED_MESSAGE = "Applied a tax of %1$f percent (%2$.2f) to the received amount for a resulting price of %3$.2f";
private static final String TAX_SENT_MESSAGE = "Reduced buy price by tax of %1$f percent (%2$.2f) for a resulting price of %3$.2f as the buyer has the buy tax bypass permission"; private static final String TAX_SENT_MESSAGE = "Reduced buy price by tax of %1$f percent (%2$.2f) for a resulting price of %3$.2f as the buyer has the buy tax bypass permission";
private static float getTax(UUID partner) { private static double getTax(UUID partner) {
float taxAmount = NameManager.isAdminShop(partner) || NameManager.isServerEconomyAccount(partner) double taxAmount = NameManager.isAdminShop(partner) || NameManager.isServerEconomyAccount(partner)
? Properties.SERVER_TAX_AMOUNT : Properties.TAX_AMOUNT; ? Properties.SERVER_TAX_AMOUNT : Properties.TAX_AMOUNT;
if (taxAmount == 0) { if (taxAmount == 0) {
@ -31,7 +31,7 @@ public class TaxModule implements Listener {
return taxAmount; return taxAmount;
} }
private static BigDecimal getTaxAmount(BigDecimal price, float taxAmount) { private static BigDecimal getTaxAmount(BigDecimal price, double taxAmount) {
return price.multiply(BigDecimal.valueOf(taxAmount)).divide(BigDecimal.valueOf(100), Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP); return price.multiply(BigDecimal.valueOf(taxAmount)).divide(BigDecimal.valueOf(100), Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP);
} }
@ -41,7 +41,7 @@ public class TaxModule implements Listener {
return; return;
} }
float taxAmount = getTax(event.getPartner()); double taxAmount = getTax(event.getPartner());
if (taxAmount == 0) { if (taxAmount == 0) {
return; return;
} }
@ -57,20 +57,20 @@ public class TaxModule implements Listener {
NameManager.getServerEconomyAccount().getUuid(), NameManager.getServerEconomyAccount().getUuid(),
event.getWorld())); event.getWorld()));
} }
ChestShop.getBukkitLogger().info(String.format(TAX_RECEIVED_MESSAGE, taxAmount, tax, taxedAmount)); ChestShop.getShopLogger().info(String.format(TAX_RECEIVED_MESSAGE, taxAmount, tax, taxedAmount));
} }
} else if (event.getDirection() == CurrencyTransferEvent.Direction.PARTNER && Permission.has(event.getInitiator(), Permission.NO_BUY_TAX)) { } else if (event.getDirection() == CurrencyTransferEvent.Direction.PARTNER && Permission.has(event.getInitiator(), Permission.NO_BUY_TAX)) {
// Reduce paid amount as the buyer has permission to not pay taxes // Reduce paid amount as the buyer has permission to not pay taxes
BigDecimal taxSent = getTaxAmount(event.getAmountSent(), taxAmount); BigDecimal taxSent = getTaxAmount(event.getAmountSent(), taxAmount);
BigDecimal taxedSentAmount = event.getAmountSent().subtract(taxSent); BigDecimal taxedSentAmount = event.getAmountSent().subtract(taxSent);
event.setAmountSent(taxedSentAmount); event.setAmountSent(taxedSentAmount);
ChestShop.getBukkitLogger().info(String.format(TAX_SENT_MESSAGE, taxAmount, taxSent, taxedSentAmount)); ChestShop.getShopLogger().info(String.format(TAX_SENT_MESSAGE, taxAmount, taxSent, taxedSentAmount));
// Reduce the amount that the seller receives anyways even though tax wasn't paid as that shouldn't make a difference for the seller // Reduce the amount that the seller receives anyways even though tax wasn't paid as that shouldn't make a difference for the seller
BigDecimal taxReceived = getTaxAmount(event.getAmountReceived(), taxAmount); BigDecimal taxReceived = getTaxAmount(event.getAmountReceived(), taxAmount);
BigDecimal taxedReceivedAmount = event.getAmountReceived().subtract(taxReceived); BigDecimal taxedReceivedAmount = event.getAmountReceived().subtract(taxReceived);
event.setAmountReceived(taxedReceivedAmount); event.setAmountReceived(taxedReceivedAmount);
ChestShop.getBukkitLogger().info(String.format(TAX_RECEIVED_MESSAGE, taxAmount, taxReceived, taxedReceivedAmount)); ChestShop.getShopLogger().info(String.format(TAX_RECEIVED_MESSAGE, taxAmount, taxReceived, taxedReceivedAmount));
} }
} }
} }

View File

@ -16,15 +16,17 @@ public class GarbageTextListener implements Listener {
public static void filterGarbage(SignChangeEvent event) { public static void filterGarbage(SignChangeEvent event) {
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
String line = event.getLine(i); String line = event.getLine(i);
StringBuilder output = new StringBuilder(line.length()); if (line != null) {
StringBuilder output = new StringBuilder(line.length());
for (char character : line.toCharArray()) { for (char character : line.toCharArray()) {
if (character < 0xF700 || character > 0xF747) { if (character < 0xF700 || character > 0xF747) {
output.append(character); output.append(character);
}
} }
}
event.setLine(i, output.toString()); event.setLine(i, output.toString());
}
} }
} }
} }

View File

@ -1,11 +1,15 @@
package com.Acrobot.ChestShop.Listeners.Item; package com.Acrobot.ChestShop.Listeners.Item;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Listeners.Modules.StockCounterModule;
import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.Acrobot.ChestShop.Signs.ChestShopSign;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.Hopper;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.inventory.InventoryHolder;
import static com.Acrobot.Breeze.Utils.ImplementationAdapter.getHolder; import static com.Acrobot.Breeze.Utils.ImplementationAdapter.getHolder;
@ -16,14 +20,15 @@ public class ItemMoveListener implements Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public static void onItemMove(InventoryMoveItemEvent event) { public static void onItemMove(InventoryMoveItemEvent event) {
if (event.getSource() == null || getHolder(event.getDestination(), false) instanceof BlockState) { InventoryHolder destinationHolder = getHolder(event.getDestination(), false);
return; InventoryHolder sourceHolder = getHolder(event.getSource(), false);
}
if (!ChestShopSign.isShopBlock(getHolder(event.getSource(), false))) { if (!(destinationHolder instanceof BlockState) && ChestShopSign.isShopBlock(sourceHolder)) {
return; event.setCancelled(true);
} else if (Properties.USE_STOCK_COUNTER && ChestShopSign.isShopBlock(destinationHolder) && sourceHolder instanceof Hopper) {
StockCounterModule.updateCounterOnItemMoveEvent(event.getItem(), destinationHolder);
} }
event.setCancelled(true);
} }
} }

View File

@ -11,7 +11,7 @@ public class ItemStringListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL) @EventHandler(priority = EventPriority.NORMAL)
public static void calculateItemString(ItemStringQueryEvent event) { public static void calculateItemString(ItemStringQueryEvent event) {
if (event.getItemString() == null) { if (event.getItemString() == null) {
event.setItemString(MaterialUtil.getName(event.getItem(), 0)); event.setItemString(MaterialUtil.getName(event.getItem(), event.getMaxWidth()));
} }
} }

View File

@ -1,19 +1,30 @@
package com.Acrobot.ChestShop.Listeners; package com.Acrobot.ChestShop.Listeners;
import com.Acrobot.Breeze.Utils.StringUtil; import com.Acrobot.Breeze.Utils.StringUtil;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Commands.ItemInfo;
import com.Acrobot.ChestShop.Events.ItemInfoEvent; import com.Acrobot.ChestShop.Events.ItemInfoEvent;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Material; import org.bukkit.Color;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.AxolotlBucketMeta;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.BundleMeta;
import org.bukkit.inventory.meta.CrossbowMeta;
import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.KnowledgeBookMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.inventory.meta.Repairable; import org.bukkit.inventory.meta.Repairable;
import org.bukkit.potion.Potion; import org.bukkit.inventory.meta.TropicalFishBucketMeta;
import org.bukkit.map.MapView;
import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffect;
import java.util.Map; import java.util.Map;
@ -21,34 +32,99 @@ import java.util.Map;
import static com.Acrobot.Breeze.Utils.NumberUtil.toRoman; import static com.Acrobot.Breeze.Utils.NumberUtil.toRoman;
import static com.Acrobot.Breeze.Utils.NumberUtil.toTime; import static com.Acrobot.Breeze.Utils.NumberUtil.toTime;
import static com.Acrobot.Breeze.Utils.StringUtil.capitalizeFirstLetter; import static com.Acrobot.Breeze.Utils.StringUtil.capitalizeFirstLetter;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_axolotl_variant;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_book; import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_book;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_book_generation; import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_book_generation;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_bundle_items;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_crossbow_projectile;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_crossbow_projectiles;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_leather_color;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_lore; import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_lore;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_map_location;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_map_view;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_recipes;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_repaircost; import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_repaircost;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_tropical_fish;
/** /**
* @author Acrobot * @author Acrobot
*/ */
public class ItemInfoListener implements Listener { public class ItemInfoListener implements Listener {
// Register version dependent listeners
static {
try {
Class.forName("org.bukkit.inventory.meta.AxolotlBucketMeta");
ChestShop.registerListener(new Listener() {
@EventHandler
public void addAxolotlInfo(ItemInfoEvent event) {
if (event.getItem().hasItemMeta()) {
ItemMeta meta = event.getItem().getItemMeta();
if (meta instanceof AxolotlBucketMeta) {
iteminfo_axolotl_variant.send(event.getSender(), "variant", capitalizeFirstLetter(((AxolotlBucketMeta) meta).getVariant().name(), '_'));
}
}
}
});
} catch (ClassNotFoundException | NoClassDefFoundError ignored) {}
try {
Class.forName("org.bukkit.inventory.meta.BundleMeta");
ChestShop.registerListener(new Listener() {
@EventHandler
public void addAxolotlInfo(ItemInfoEvent event) {
if (event.getItem().hasItemMeta()) {
ItemMeta meta = event.getItem().getItemMeta();
if (meta instanceof BundleMeta) {
iteminfo_bundle_items.send(event.getSender(), "itemcount", String.valueOf(((BundleMeta) meta).getItems().size()));
}
}
}
});
} catch (ClassNotFoundException | NoClassDefFoundError ignored) {}
try {
Class.forName("org.bukkit.inventory.meta.CrossbowMeta");
ChestShop.registerListener(new Listener() {
@EventHandler
public void addCrossBowInfo(ItemInfoEvent event) {
if (event.getItem().hasItemMeta()) {
ItemMeta meta = event.getItem().getItemMeta();
if (meta instanceof CrossbowMeta && ((CrossbowMeta) meta).hasChargedProjectiles()) {
iteminfo_crossbow_projectiles.send(event.getSender());
for (ItemStack chargedProjectile : ((CrossbowMeta) meta).getChargedProjectiles()) {
ItemInfo.sendItemName(event.getSender(), chargedProjectile, iteminfo_crossbow_projectile);
ChestShop.callEvent(new ItemInfoEvent(event.getSender(), chargedProjectile));
event.getSender().sendMessage(ChatColor.GRAY + "---");
}
}
}
}
});
} catch (ClassNotFoundException | NoClassDefFoundError ignored) {}
}
@EventHandler @EventHandler
public static void addRepairCost(ItemInfoEvent event) { public static void addRepairCost(ItemInfoEvent event) {
ItemMeta meta = event.getItem().getItemMeta(); if (event.getItem().hasItemMeta()) {
if (meta instanceof Repairable && ((Repairable) meta).getRepairCost() > 0) { ItemMeta meta = event.getItem().getItemMeta();
iteminfo_repaircost.send(event.getSender(), "cost", String.valueOf(((Repairable) meta).getRepairCost())); if (meta instanceof Repairable && ((Repairable) meta).getRepairCost() > 0) {
iteminfo_repaircost.send(event.getSender(), "cost", String.valueOf(((Repairable) meta).getRepairCost()));
}
} }
} }
@EventHandler @EventHandler
public static void addEnchantment(ItemInfoEvent event) { public static void addEnchantment(ItemInfoEvent event) {
ItemStack item = event.getItem(); ItemStack item = event.getItem();
ItemMeta meta = item.getItemMeta();
CommandSender sender = event.getSender(); CommandSender sender = event.getSender();
for (Map.Entry<Enchantment, Integer> enchantment : item.getEnchantments().entrySet()) { for (Map.Entry<Enchantment, Integer> enchantment : item.getEnchantments().entrySet()) {
sender.sendMessage(ChatColor.AQUA + capitalizeFirstLetter(enchantment.getKey().getName(), '_') + ' ' + toRoman(enchantment.getValue())); sender.sendMessage(ChatColor.AQUA + capitalizeFirstLetter(enchantment.getKey().getName(), '_') + ' ' + toRoman(enchantment.getValue()));
} }
if (!item.hasItemMeta()) {
return;
}
ItemMeta meta = item.getItemMeta();
if (meta instanceof EnchantmentStorageMeta) { if (meta instanceof EnchantmentStorageMeta) {
for (Map.Entry<Enchantment, Integer> enchantment : ((EnchantmentStorageMeta) meta).getStoredEnchants().entrySet()) { for (Map.Entry<Enchantment, Integer> enchantment : ((EnchantmentStorageMeta) meta).getStoredEnchants().entrySet()) {
sender.sendMessage(ChatColor.YELLOW + capitalizeFirstLetter(enchantment.getKey().getName(), '_') + ' ' + toRoman(enchantment.getValue())); sender.sendMessage(ChatColor.YELLOW + capitalizeFirstLetter(enchantment.getKey().getName(), '_') + ' ' + toRoman(enchantment.getValue()));
@ -56,49 +132,110 @@ public class ItemInfoListener implements Listener {
} }
} }
@EventHandler
public static void addLeatherColor(ItemInfoEvent event) {
if (event.getItem().hasItemMeta()) {
ItemMeta meta = event.getItem().getItemMeta();
if (meta instanceof LeatherArmorMeta) {
Color color = ((LeatherArmorMeta) meta).getColor();
iteminfo_leather_color.send(event.getSender(),
"colorred", String.valueOf(color.getRed()),
"colorgreen", String.valueOf(color.getGreen()),
"colorblue", String.valueOf(color.getBlue()),
"colorhex", Integer.toHexString(color.asRGB())
);
}
}
}
@EventHandler
public static void addRecipes(ItemInfoEvent event) {
if (event.getItem().hasItemMeta()) {
ItemMeta meta = event.getItem().getItemMeta();
if (meta instanceof KnowledgeBookMeta && !((KnowledgeBookMeta) meta).getRecipes().isEmpty()) {
iteminfo_recipes.send(event.getSender());
for (NamespacedKey recipe : ((KnowledgeBookMeta) meta).getRecipes()) {
event.getSender().sendMessage(ChatColor.GRAY + recipe.toString());
}
}
}
}
@EventHandler
public static void addTropicalFishInfo(ItemInfoEvent event) {
if (event.getItem().hasItemMeta()) {
ItemMeta meta = event.getItem().getItemMeta();
if (meta instanceof TropicalFishBucketMeta && ((TropicalFishBucketMeta) meta).hasVariant()) {
iteminfo_tropical_fish.send(event.getSender(),
"pattern", capitalizeFirstLetter(((TropicalFishBucketMeta) meta).getPattern().name()),
"patterncolor", capitalizeFirstLetter(((TropicalFishBucketMeta) meta).getPatternColor().name()),
"bodycolor", capitalizeFirstLetter(((TropicalFishBucketMeta) meta).getBodyColor().name())
);
}
}
}
@EventHandler
public static void addMapInfo(ItemInfoEvent event) {
if (event.getItem().hasItemMeta()) {
ItemMeta meta = event.getItem().getItemMeta();
if (meta instanceof MapMeta) {
if (((MapMeta) meta).getMapView() != null) {
MapView mapView = ((MapMeta) meta).getMapView();
iteminfo_map_view.send(event.getSender(),
"id", String.valueOf(mapView.getId()),
"x", String.valueOf(mapView.getCenterX()),
"z", String.valueOf(mapView.getCenterZ()),
"world", mapView.getWorld() != null ? String.valueOf(mapView.getWorld().getName()) : "unknown",
"scale", capitalizeFirstLetter(mapView.getScale().name(), '_'),
"locked", String.valueOf(mapView.isLocked())
);
}
if (((MapMeta) meta).hasLocationName()) {
iteminfo_map_location.send(event.getSender(), "location", String.valueOf(((MapMeta) meta).getLocationName()));
}
}
}
}
@EventHandler @EventHandler
public static void addPotionInfo(ItemInfoEvent event) { public static void addPotionInfo(ItemInfoEvent event) {
ItemStack item = event.getItem(); ItemStack item = event.getItem();
if (!item.hasItemMeta()) {
if (item.getType() != Material.POTION || item.getDurability() == 0) {
return; return;
} }
Potion potion; ItemMeta meta = item.getItemMeta();
if (!(meta instanceof PotionMeta)) {
try {
potion = Potion.fromItemStack(item);
} catch (IllegalArgumentException ex) {
return; return;
} }
StringBuilder message = new StringBuilder(50); PotionMeta potionMeta = (PotionMeta) meta;
message.append(ChatColor.GRAY);
if (potion.getType() == null) {
return;
}
if (potion.isSplash()) {
message.append("Splash ");
}
message.append("Potion of ");
message.append(capitalizeFirstLetter(potion.getType().name(), '_')).append(' ');
message.append(toRoman(potion.getLevel()));
CommandSender sender = event.getSender(); CommandSender sender = event.getSender();
sender.sendMessage(message.toString()); if (potionMeta.getBasePotionType() != null) {
StringBuilder message = new StringBuilder(50);
for (PotionEffect effect : potion.getEffects()) { message.append(ChatColor.GRAY);
sender.sendMessage(ChatColor.DARK_GRAY + capitalizeFirstLetter(effect.getType().getName(), '_') + ' ' + toTime(effect.getDuration() / 20));
message.append(capitalizeFirstLetter(item.getType().name(), '_')).append(" of ");
message.append(capitalizeFirstLetter(potionMeta.getBasePotionType().getKey().getKey(), '_')).append(' ');
sender.sendMessage(message.toString());
}
for (PotionEffect effect : potionMeta.getCustomEffects()) {
sender.sendMessage(ChatColor.DARK_GRAY + capitalizeFirstLetter(effect.getType().getKey().getKey(), '_')
+ ' ' + (effect.getAmplifier() + 1) + ' ' + toTime(effect.getDuration() / 20));
} }
} }
@EventHandler @EventHandler
public static void addBookInfo(ItemInfoEvent event) { public static void addBookInfo(ItemInfoEvent event) {
if (!event.getItem().hasItemMeta()) {
return;
}
ItemMeta meta = event.getItem().getItemMeta(); ItemMeta meta = event.getItem().getItemMeta();
if (meta instanceof BookMeta) { if (meta instanceof BookMeta) {
BookMeta book = (BookMeta) meta; BookMeta book = (BookMeta) meta;

View File

@ -17,6 +17,7 @@ import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import static com.Acrobot.ChestShop.Events.TransactionEvent.TransactionType.BUY; import static com.Acrobot.ChestShop.Events.TransactionEvent.TransactionType.BUY;
@ -44,7 +45,7 @@ public class DiscountModule implements Listener {
try { try {
config.save(ChestShop.loadFile("discounts.yml")); config.save(ChestShop.loadFile("discounts.yml"));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while loading discounts config", e);
} }
groupList = config.getKeys(false); groupList = config.getKeys(false);

View File

@ -16,6 +16,9 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import static com.Acrobot.Breeze.Utils.StringUtil.getMinecraftStringWidth;
/** /**
* @author Acrobot * @author Acrobot
@ -49,7 +52,7 @@ public class ItemAliasModule implements Listener {
configuration.options().copyDefaults(true); configuration.options().copyDefaults(true);
configuration.save(ChestShop.loadFile("itemAliases.yml")); configuration.save(ChestShop.loadFile("itemAliases.yml"));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while saving item aliases config", e);
} }
} }
@ -118,6 +121,13 @@ public class ItemAliasModule implements Listener {
} }
if (newCode != null) { if (newCode != null) {
if (event.getMaxWidth() > 0) {
int width = getMinecraftStringWidth(newCode);
if (width > event.getMaxWidth()) {
ChestShop.getBukkitLogger().warning("Can't use configured alias " + newCode + " as it's width (" + width + ") was wider than the allowed max width of " + event.getMaxWidth());
return;
}
}
event.setItemString(newCode); event.setItemString(newCode);
} }
} }

View File

@ -6,11 +6,13 @@ import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Events.ChestShopReloadEvent; import com.Acrobot.ChestShop.Events.ChestShopReloadEvent;
import com.Acrobot.ChestShop.Events.ItemParseEvent; import com.Acrobot.ChestShop.Events.ItemParseEvent;
import com.Acrobot.ChestShop.Events.PreShopCreationEvent; import com.Acrobot.ChestShop.Events.PreShopCreationEvent;
import com.Acrobot.ChestShop.Permission;
import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.Acrobot.ChestShop.Signs.ChestShopSign;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -21,10 +23,8 @@ import java.math.BigDecimal;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
import static com.Acrobot.ChestShop.Events.PreShopCreationEvent.CreationOutcome.BUY_PRICE_ABOVE_MAX; import static com.Acrobot.ChestShop.Events.PreShopCreationEvent.CreationOutcome.*;
import static com.Acrobot.ChestShop.Events.PreShopCreationEvent.CreationOutcome.BUY_PRICE_BELOW_MIN; import static com.Acrobot.ChestShop.Permission.*;
import static com.Acrobot.ChestShop.Events.PreShopCreationEvent.CreationOutcome.SELL_PRICE_ABOVE_MAX;
import static com.Acrobot.ChestShop.Events.PreShopCreationEvent.CreationOutcome.SELL_PRICE_BELOW_MIN;
/** /**
* @author Acrobot * @author Acrobot
@ -58,7 +58,7 @@ public class PriceRestrictionModule implements Listener {
configuration.options().copyDefaults(true); configuration.options().copyDefaults(true);
configuration.save(ChestShop.loadFile("priceLimits.yml")); configuration.save(ChestShop.loadFile("priceLimits.yml"));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while loading priceLimits.yml", e);
} }
} else if (!configuration.getBoolean("uses_materials")) { } else if (!configuration.getBoolean("uses_materials")) {
Material testMat = Material.matchMaterial("1"); Material testMat = Material.matchMaterial("1");
@ -73,7 +73,7 @@ public class PriceRestrictionModule implements Listener {
configuration.save(file); configuration.save(file);
ChestShop.getBukkitLogger().log(Level.INFO, "Conversion finished!"); ChestShop.getBukkitLogger().log(Level.INFO, "Conversion finished!");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while converting priceLimits.yml", e);
} }
} else { } else {
ChestShop.getBukkitLogger().log(Level.WARNING, "Could not convert numeric IDs in priceLimits.yml to Material names!"); ChestShop.getBukkitLogger().log(Level.WARNING, "Could not convert numeric IDs in priceLimits.yml to Material names!");
@ -106,6 +106,7 @@ public class PriceRestrictionModule implements Listener {
ItemParseEvent parseEvent = new ItemParseEvent(ChestShopSign.getItem(event.getSignLines())); ItemParseEvent parseEvent = new ItemParseEvent(ChestShopSign.getItem(event.getSignLines()));
Bukkit.getPluginManager().callEvent(parseEvent); Bukkit.getPluginManager().callEvent(parseEvent);
ItemStack material = parseEvent.getItem(); ItemStack material = parseEvent.getItem();
Player player = event.getPlayer();
if (material == null) { if (material == null) {
return; return;
@ -124,15 +125,17 @@ public class PriceRestrictionModule implements Listener {
BigDecimal buyPrice = PriceUtil.getExactBuyPrice(priceLine); BigDecimal buyPrice = PriceUtil.getExactBuyPrice(priceLine);
BigDecimal minBuyPrice = BigDecimal.valueOf(configuration.getDouble("min.buy_price." + itemType) * amount); BigDecimal minBuyPrice = BigDecimal.valueOf(configuration.getDouble("min.buy_price." + itemType) * amount);
if (isValid("min.buy_price." + itemType) && buyPrice.compareTo(minBuyPrice) < 0) { if (isValid("min.buy_price." + itemType) && buyPrice.compareTo(minBuyPrice) < 0
&& !Permission.has(player, NOLIMIT_MIN_BUY) && !Permission.has(player, NOLIMIT_MIN_BUY_ID + itemType)) {
event.setOutcome(BUY_PRICE_BELOW_MIN); event.setOutcome(BUY_PRICE_BELOW_MIN);
Messages.BUY_PRICE_BELOW_MIN.sendWithPrefix(event.getPlayer(), "price", buyPrice.toPlainString(), "minprice", minBuyPrice.toPlainString()); Messages.BUY_PRICE_BELOW_MIN.sendWithPrefix(player, "price", buyPrice.toPlainString(), "minprice", minBuyPrice.toPlainString());
} }
BigDecimal maxBuyPrice = BigDecimal.valueOf(configuration.getDouble("max.buy_price." + itemType) * amount); BigDecimal maxBuyPrice = BigDecimal.valueOf(configuration.getDouble("max.buy_price." + itemType) * amount);
if (isValid("max.buy_price." + itemType) && buyPrice.compareTo(maxBuyPrice) > 0) { if (isValid("max.buy_price." + itemType) && buyPrice.compareTo(maxBuyPrice) > 0
&& !Permission.has(player, NOLIMIT_MAX_BUY) && !Permission.has(player, NOLIMIT_MAX_BUY_ID + itemType)) {
event.setOutcome(BUY_PRICE_ABOVE_MAX); event.setOutcome(BUY_PRICE_ABOVE_MAX);
Messages.BUY_PRICE_ABOVE_MAX.sendWithPrefix(event.getPlayer(), "price", buyPrice.toPlainString(), "maxprice", maxBuyPrice.toPlainString()); Messages.BUY_PRICE_ABOVE_MAX.sendWithPrefix(player, "price", buyPrice.toPlainString(), "maxprice", maxBuyPrice.toPlainString());
} }
} }
@ -140,15 +143,17 @@ public class PriceRestrictionModule implements Listener {
BigDecimal sellPrice = PriceUtil.getExactSellPrice(priceLine); BigDecimal sellPrice = PriceUtil.getExactSellPrice(priceLine);
BigDecimal minSellPrice = BigDecimal.valueOf(configuration.getDouble("min.sell_price." + itemType) * amount); BigDecimal minSellPrice = BigDecimal.valueOf(configuration.getDouble("min.sell_price." + itemType) * amount);
if (isValid("min.sell_price." + itemType) && sellPrice.compareTo(minSellPrice) < 0) { if (isValid("min.sell_price." + itemType) && sellPrice.compareTo(minSellPrice) < 0
&& !Permission.has(player, NOLIMIT_MIN_SELL) && !Permission.has(player, NOLIMIT_MIN_SELL_ID + itemType)) {
event.setOutcome(SELL_PRICE_BELOW_MIN); event.setOutcome(SELL_PRICE_BELOW_MIN);
Messages.SELL_PRICE_BELOW_MIN.sendWithPrefix(event.getPlayer(), "price", sellPrice.toPlainString(), "minprice", minSellPrice.toPlainString()); Messages.SELL_PRICE_BELOW_MIN.sendWithPrefix(player, "price", sellPrice.toPlainString(), "minprice", minSellPrice.toPlainString());
} }
BigDecimal maxSellPrice = BigDecimal.valueOf(configuration.getDouble("max.sell_price." + itemType) * amount); BigDecimal maxSellPrice = BigDecimal.valueOf(configuration.getDouble("max.sell_price." + itemType) * amount);
if (isValid("max.sell_price." + itemType) && sellPrice.compareTo(maxSellPrice) > 0) { if (isValid("max.sell_price." + itemType) && sellPrice.compareTo(maxSellPrice) > 0
&& !Permission.has(player, NOLIMIT_MAX_SELL) && !Permission.has(player, NOLIMIT_MAX_SELL_ID + itemType)) {
event.setOutcome(SELL_PRICE_ABOVE_MAX); event.setOutcome(SELL_PRICE_ABOVE_MAX);
Messages.SELL_PRICE_ABOVE_MAX.sendWithPrefix(event.getPlayer(), "price", sellPrice.toPlainString(), "maxprice", maxSellPrice.toPlainString()); Messages.SELL_PRICE_ABOVE_MAX.sendWithPrefix(player, "price", sellPrice.toPlainString(), "maxprice", maxSellPrice.toPlainString());
} }
} }
} }

View File

@ -1,6 +1,7 @@
package com.Acrobot.ChestShop.Listeners.Modules; package com.Acrobot.ChestShop.Listeners.Modules;
import com.Acrobot.Breeze.Utils.InventoryUtil; import com.Acrobot.Breeze.Utils.InventoryUtil;
import com.Acrobot.Breeze.Utils.MaterialUtil;
import com.Acrobot.Breeze.Utils.QuantityUtil; import com.Acrobot.Breeze.Utils.QuantityUtil;
import com.Acrobot.ChestShop.ChestShop; import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Configuration.Properties;
@ -10,6 +11,7 @@ import com.Acrobot.ChestShop.Events.TransactionEvent;
import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.Acrobot.ChestShop.Signs.ChestShopSign;
import com.Acrobot.ChestShop.Utils.uBlock; import com.Acrobot.ChestShop.Utils.uBlock;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.block.Container; import org.bukkit.block.Container;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -18,6 +20,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.IllegalFormatException; import java.util.IllegalFormatException;
@ -66,11 +69,16 @@ public class StockCounterModule implements Listener {
@EventHandler @EventHandler
public static void onInventoryClose(InventoryCloseEvent event) { public static void onInventoryClose(InventoryCloseEvent event) {
if (event.getInventory().getType() == InventoryType.ENDER_CHEST || event.getInventory().getLocation() == null || !uBlock.couldBeShopContainer(event.getInventory().getLocation().getBlock())) { if (event.getInventory().getType() == InventoryType.ENDER_CHEST || event.getInventory().getLocation() == null) {
return; return;
} }
for (Sign shopSign : uBlock.findConnectedShopSigns(getHolder(event.getInventory(), false))) { InventoryHolder holder = getHolder(event.getInventory(), false);
if (!uBlock.couldBeShopContainer(holder)) {
return;
}
for (Sign shopSign : uBlock.findConnectedShopSigns(holder)) {
if (!Properties.USE_STOCK_COUNTER if (!Properties.USE_STOCK_COUNTER
|| (Properties.FORCE_UNLIMITED_ADMIN_SHOP && ChestShopSign.isAdminShop(shopSign))) { || (Properties.FORCE_UNLIMITED_ADMIN_SHOP && ChestShopSign.isAdminShop(shopSign))) {
if (QuantityUtil.quantityLineContainsCounter(ChestShopSign.getQuantityLine(shopSign))) { if (QuantityUtil.quantityLineContainsCounter(ChestShopSign.getQuantityLine(shopSign))) {
@ -118,7 +126,13 @@ public class StockCounterModule implements Listener {
} }
} }
public static void updateCounterOnQuantityLine(Sign sign, Inventory chestShopInventory) { /**
* Update the stock counter on the sign's quantity line
* @param sign The sign to update
* @param chestShopInventory The inventory to search in
* @param extraItems The extra items to add in the search
*/
public static void updateCounterOnQuantityLine(Sign sign, Inventory chestShopInventory, ItemStack... extraItems) {
ItemStack itemTradedByShop = determineItemTradedByShop(sign); ItemStack itemTradedByShop = determineItemTradedByShop(sign);
if (itemTradedByShop == null) { if (itemTradedByShop == null) {
return; return;
@ -133,10 +147,25 @@ public class StockCounterModule implements Listener {
int numTradedItemsInChest = InventoryUtil.getAmount(itemTradedByShop, chestShopInventory); int numTradedItemsInChest = InventoryUtil.getAmount(itemTradedByShop, chestShopInventory);
for (ItemStack extraStack : extraItems) {
if (!MaterialUtil.equals(extraStack, itemTradedByShop)) {
continue;
}
numTradedItemsInChest += extraStack.getAmount();
}
sign.setLine(QUANTITY_LINE, String.format(PRICE_LINE_WITH_COUNT, quantity, numTradedItemsInChest)); sign.setLine(QUANTITY_LINE, String.format(PRICE_LINE_WITH_COUNT, quantity, numTradedItemsInChest));
sign.update(true); sign.update(true);
} }
public static void updateCounterOnItemMoveEvent(ItemStack toAdd, InventoryHolder destinationHolder) {
Block shopBlock = ChestShopSign.getShopBlock(destinationHolder);
Sign connectedSign = uBlock.getConnectedSign(shopBlock);
updateCounterOnQuantityLine(connectedSign, destinationHolder.getInventory(), toAdd);
}
public static void removeCounterFromQuantityLine(Sign sign) { public static void removeCounterFromQuantityLine(Sign sign) {
int quantity; int quantity;
try { try {

View File

@ -1,6 +1,5 @@
package com.Acrobot.ChestShop.Listeners.Player; package com.Acrobot.ChestShop.Listeners.Player;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -23,7 +22,7 @@ public class PlayerConnect implements Listener {
final PlayerDTO playerDTO = new PlayerDTO(event.getPlayer()); final PlayerDTO playerDTO = new PlayerDTO(event.getPlayer());
Bukkit.getScheduler().runTaskAsynchronously(ChestShop.getPlugin(), () -> { ChestShop.runInAsyncThread(() -> {
if (NameManager.getAccount(playerDTO.getUniqueId()) != null) { if (NameManager.getAccount(playerDTO.getUniqueId()) != null) {
NameManager.storeUsername(playerDTO); NameManager.storeUsername(playerDTO);
} }

View File

@ -36,6 +36,7 @@ import org.bukkit.inventory.ItemStack;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Level; import java.util.logging.Level;
@ -81,7 +82,7 @@ public class PlayerInteract implements Listener {
} }
} }
if (!isSign(block) || player.getInventory().getItemInMainHand().getType().name().contains("SIGN")) // Blocking accidental sign edition if (!isSign(block))
return; return;
Sign sign = (Sign) getState(block, false); Sign sign = (Sign) getState(block, false);
@ -93,6 +94,7 @@ public class PlayerInteract implements Listener {
if (ChestShopSign.hasPermission(player, OTHER_NAME_CREATE, sign)) { if (ChestShopSign.hasPermission(player, OTHER_NAME_CREATE, sign)) {
ItemStack item = player.getInventory().getItemInMainHand(); ItemStack item = player.getInventory().getItemInMainHand();
if (!MaterialUtil.isEmpty(item)) { if (!MaterialUtil.isEmpty(item)) {
event.setCancelled(true);
String itemCode; String itemCode;
try { try {
itemCode = ItemUtil.getSignName(item); itemCode = ItemUtil.getSignName(item);
@ -108,11 +110,10 @@ public class PlayerInteract implements Listener {
com.Acrobot.ChestShop.ChestShop.callEvent(changeEvent); com.Acrobot.ChestShop.ChestShop.callEvent(changeEvent);
if (!changeEvent.isCancelled()) { if (!changeEvent.isCancelled()) {
for (byte i = 0; i < changeEvent.getLines().length; ++i) { for (byte i = 0; i < changeEvent.getLines().length; ++i) {
sign.setLine(i, changeEvent.getLine(i)); String line = changeEvent.getLine(i);
sign.setLine(i, line != null ? line : "");
} }
sign.update(); sign.update();
} else {
event.setCancelled(true);
} }
} else { } else {
Messages.NO_ITEM_IN_HAND.sendWithPrefix(player); Messages.NO_ITEM_IN_HAND.sendWithPrefix(player);
@ -125,7 +126,10 @@ public class PlayerInteract implements Listener {
if (!AccessToggle.isIgnoring(player) && ChestShopSign.canAccess(player, sign) && !ChestShopSign.isAdminShop(sign)) { if (!AccessToggle.isIgnoring(player) && ChestShopSign.canAccess(player, sign) && !ChestShopSign.isAdminShop(sign)) {
if (Properties.IGNORE_ACCESS_PERMS || ChestShopSign.isOwner(player, sign)) { if (Properties.IGNORE_ACCESS_PERMS || ChestShopSign.isOwner(player, sign)) {
if ((player.getInventory().getItemInMainHand().getType().name().endsWith("DYE") if (player.getInventory().getItemInMainHand().getType().name().contains("SIGN") && action == RIGHT_CLICK_BLOCK) {
// Allow editing of sign (if supported)
return;
} else if ((player.getInventory().getItemInMainHand().getType().name().endsWith("DYE")
|| player.getInventory().getItemInMainHand().getType().name().endsWith("INK_SAC")) || player.getInventory().getItemInMainHand().getType().name().endsWith("INK_SAC"))
&& action == RIGHT_CLICK_BLOCK) { && action == RIGHT_CLICK_BLOCK) {
if (Properties.SIGN_DYING) { if (Properties.SIGN_DYING) {
@ -145,6 +149,10 @@ public class PlayerInteract implements Listener {
} }
// don't allow owners or people with access to buy/sell at this shop // don't allow owners or people with access to buy/sell at this shop
Messages.TRADE_DENIED_ACCESS_PERMS.sendWithPrefix(player); Messages.TRADE_DENIED_ACCESS_PERMS.sendWithPrefix(player);
if (action == RIGHT_CLICK_BLOCK) {
// don't allow editing
event.setCancelled(true);
}
return; return;
} }
} }
@ -215,30 +223,31 @@ public class PlayerInteract implements Listener {
int amount = -1; int amount = -1;
try { try {
amount = ChestShopSign.getQuantity(sign); amount = ChestShopSign.getQuantity(sign);
} catch (NumberFormatException notANumber) {} } catch (NumberFormatException ignored) {} // There is no quantity number on the sign
if (amount < 1 || amount > Properties.MAX_SHOP_AMOUNT) { if (amount < 1 || amount > Properties.MAX_SHOP_AMOUNT) {
Messages.INVALID_SHOP_PRICE.sendWithPrefix(player); Messages.INVALID_SHOP_PRICE.sendWithPrefix(player);
return null; return null;
} }
BigDecimal pricePerItem = price.divide(BigDecimal.valueOf(amount), MathContext.DECIMAL128);
if (Properties.SHIFT_SELLS_IN_STACKS && player.isSneaking() && !price.equals(PriceUtil.NO_PRICE) && isAllowedForShift(action == buy)) { if (Properties.SHIFT_SELLS_IN_STACKS && player.isSneaking() && !price.equals(PriceUtil.NO_PRICE) && isAllowedForShift(action == buy)) {
int newAmount = adminShop ? InventoryUtil.getMaxStackSize(item) : getStackAmount(item, ownerInventory, player, action); int newAmount = adminShop ? InventoryUtil.getMaxStackSize(item) : getStackAmount(item, ownerInventory, player, action);
if (newAmount > 0) { if (newAmount > 0) {
price = price.divide(BigDecimal.valueOf(amount), MathContext.DECIMAL128).multiply(BigDecimal.valueOf(newAmount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP); price = pricePerItem.multiply(BigDecimal.valueOf(newAmount)).setScale(Properties.PRICE_PRECISION, RoundingMode.HALF_UP);
amount = newAmount; amount = newAmount;
} }
} else if (Properties.SHIFT_SELLS_EVERYTHING && player.isSneaking() && !price.equals(PriceUtil.NO_PRICE) && isAllowedForShift(action == buy)) { } else if (Properties.SHIFT_SELLS_EVERYTHING && player.isSneaking() && !price.equals(PriceUtil.NO_PRICE) && isAllowedForShift(action == buy)) {
if (action != buy) { if (action != buy) {
int newAmount = InventoryUtil.getAmount(item, player.getInventory()); int newAmount = InventoryUtil.getAmount(item, player.getInventory());
if (newAmount > 0) { if (newAmount > 0) {
price = price.divide(BigDecimal.valueOf(amount), MathContext.DECIMAL128).multiply(BigDecimal.valueOf(newAmount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP); price = pricePerItem.multiply(BigDecimal.valueOf(newAmount)).setScale(Properties.PRICE_PRECISION, RoundingMode.HALF_UP);
amount = newAmount; amount = newAmount;
} }
} else if (!adminShop && ownerInventory != null) { } else if (!adminShop && ownerInventory != null) {
int newAmount = InventoryUtil.getAmount(item, ownerInventory); int newAmount = InventoryUtil.getAmount(item, ownerInventory);
if (newAmount > 0) { if (newAmount > 0) {
price = price.divide(BigDecimal.valueOf(amount), MathContext.DECIMAL128).multiply(BigDecimal.valueOf(newAmount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP); price = pricePerItem.multiply(BigDecimal.valueOf(newAmount)).setScale(Properties.PRICE_PRECISION, RoundingMode.HALF_UP);
amount = newAmount; amount = newAmount;
} }
} }

View File

@ -46,8 +46,14 @@ public class PlayerInventory implements Listener {
List<Block> containers = new ArrayList<>(); List<Block> containers = new ArrayList<>();
if (holder instanceof DoubleChest) { if (holder instanceof DoubleChest) {
containers.add(((BlockState) ((DoubleChest) holder).getLeftSide()).getBlock()); InventoryHolder leftSide = ((DoubleChest) holder).getLeftSide();
containers.add(((BlockState) ((DoubleChest) holder).getRightSide()).getBlock()); if (leftSide instanceof BlockState) {
containers.add(((BlockState) leftSide).getBlock());
}
InventoryHolder rightSide = ((DoubleChest) holder).getRightSide();
if (rightSide instanceof BlockState) {
containers.add(((BlockState) rightSide).getBlock());
}
} else { } else {
containers.add(((BlockState) holder).getBlock()); containers.add(((BlockState) holder).getBlock());
} }

View File

@ -16,7 +16,7 @@ public class ShopCreationLogger implements Listener {
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public static void onShopCreation(final ShopCreatedEvent event) { public static void onShopCreation(final ShopCreatedEvent event) {
ChestShop.getBukkitServer().getScheduler().runTaskAsynchronously(ChestShop.getPlugin(), () -> { ChestShop.runInAsyncThread(() -> {
String creator = event.getPlayer().getName(); String creator = event.getPlayer().getName();
String shopOwner = ChestShopSign.getOwner(event.getSignLines()); String shopOwner = ChestShopSign.getOwner(event.getSignLines());
String typeOfShop = ChestShopSign.isAdminShop(shopOwner) ? "an Admin Shop" : "a shop" + (event.createdByOwner() ? "" : " for " + event.getOwnerAccount().getName()); String typeOfShop = ChestShopSign.isAdminShop(shopOwner) ? "an Admin Shop" : "a shop" + (event.createdByOwner() ? "" : " for " + event.getOwnerAccount().getName());
@ -32,7 +32,7 @@ public class ShopCreationLogger implements Listener {
prices, prices,
location); location);
ChestShop.getBukkitLogger().info(message); ChestShop.getShopLogger().info(message);
}); });
} }
} }

View File

@ -19,7 +19,8 @@ public class EconomicModule implements Listener {
event.getExactPrice(), event.getExactPrice(),
event.getClient(), event.getClient(),
event.getOwnerAccount().getUuid(), event.getOwnerAccount().getUuid(),
event.getTransactionType() == BUY ? CurrencyTransferEvent.Direction.PARTNER : CurrencyTransferEvent.Direction.INITIATOR event.getTransactionType() == BUY ? CurrencyTransferEvent.Direction.PARTNER : CurrencyTransferEvent.Direction.INITIATOR,
event
); );
ChestShop.callEvent(currencyTransferEvent); ChestShop.callEvent(currencyTransferEvent);
if (!currencyTransferEvent.wasHandled()) { if (!currencyTransferEvent.wasHandled()) {

View File

@ -52,7 +52,9 @@ public class EmptyShopDeleter implements Listener {
sign.getBlock().setType(Material.AIR); sign.getBlock().setType(Material.AIR);
if (Properties.REMOVE_EMPTY_CHESTS && !ChestShopSign.isAdminShop(ownerInventory) && InventoryUtil.isEmpty(ownerInventory)) { if (Properties.REMOVE_EMPTY_CHESTS && !ChestShopSign.isAdminShop(ownerInventory) && InventoryUtil.isEmpty(ownerInventory)) {
connectedContainer.getBlock().setType(Material.AIR); if (connectedContainer != null) {
connectedContainer.getBlock().setType(Material.AIR);
}
} else { } else {
if (!signType.isItem()) { if (!signType.isItem()) {
try { try {

View File

@ -36,6 +36,6 @@ public class TransactionLogger implements Listener {
event.getOwnerAccount().getName(), event.getOwnerAccount().getName(),
LocationUtil.locationToString(event.getSign().getLocation())); LocationUtil.locationToString(event.getSign().getLocation()));
ChestShop.getBukkitLogger().info(message); ChestShop.getShopLogger().info(message);
} }
} }

View File

@ -6,6 +6,7 @@ import com.Acrobot.ChestShop.Commands.Toggle;
import com.Acrobot.ChestShop.Configuration.Messages; import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Economy.Economy; import com.Acrobot.ChestShop.Economy.Economy;
import com.Acrobot.ChestShop.Events.Economy.CurrencyTransferEvent;
import com.Acrobot.ChestShop.Events.TransactionEvent; import com.Acrobot.ChestShop.Events.TransactionEvent;
import com.Acrobot.ChestShop.Utils.ItemUtil; import com.Acrobot.ChestShop.Utils.ItemUtil;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -15,6 +16,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import java.math.BigDecimal;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -23,65 +25,87 @@ import java.util.Map;
*/ */
public class TransactionMessageSender implements Listener { public class TransactionMessageSender implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public static void onTransaction(TransactionEvent event) { public static void onCurrencyTransfer(CurrencyTransferEvent event) {
if (event.getTransactionType() == TransactionEvent.TransactionType.BUY) { if (event.getTransactionEvent() == null) {
return;
}
if (event.getTransactionEvent().getTransactionType() == TransactionEvent.TransactionType.BUY) {
sendBuyMessage(event); sendBuyMessage(event);
} else { } else {
sendSellMessage(event); sendSellMessage(event);
} }
} }
protected static void sendBuyMessage(TransactionEvent event) { protected static void sendBuyMessage(CurrencyTransferEvent event) {
Player player = event.getClient(); TransactionEvent transactionEvent = event.getTransactionEvent();
Player player = transactionEvent.getClient();
if (Properties.SHOW_TRANSACTION_INFORMATION_CLIENT) { if (Properties.SHOW_TRANSACTION_INFORMATION_CLIENT) {
sendMessage(player, event.getClient().getName(), Messages.YOU_BOUGHT_FROM_SHOP, event, "owner", event.getOwnerAccount().getName()); sendMessage(player, transactionEvent.getClient().getName(), Messages.YOU_BOUGHT_FROM_SHOP, event, MessageTarget.BUYER, "owner", transactionEvent.getOwnerAccount().getName());
} }
if (Properties.SHOW_TRANSACTION_INFORMATION_OWNER && !Toggle.isIgnoring(event.getOwnerAccount().getUuid())) { if (Properties.SHOW_TRANSACTION_INFORMATION_OWNER && !Toggle.isIgnoring(transactionEvent.getOwnerAccount().getUuid())) {
Player owner = Bukkit.getPlayer(event.getOwnerAccount().getUuid()); Player owner = Bukkit.getPlayer(transactionEvent.getOwnerAccount().getUuid());
sendMessage(owner, event.getOwnerAccount().getName(), Messages.SOMEBODY_BOUGHT_FROM_YOUR_SHOP, event, "buyer", player.getName()); sendMessage(owner, transactionEvent.getOwnerAccount().getName(), Messages.SOMEBODY_BOUGHT_FROM_YOUR_SHOP, event, MessageTarget.SELLER, "buyer", player.getName());
} }
} }
protected static void sendSellMessage(TransactionEvent event) { protected static void sendSellMessage(CurrencyTransferEvent event) {
Player player = event.getClient(); TransactionEvent transactionEvent = event.getTransactionEvent();
Player player = transactionEvent.getClient();
if (Properties.SHOW_TRANSACTION_INFORMATION_CLIENT) { if (Properties.SHOW_TRANSACTION_INFORMATION_CLIENT) {
sendMessage(player, event.getClient().getName(), Messages.YOU_SOLD_TO_SHOP, event, "buyer", event.getOwnerAccount().getName()); sendMessage(player, transactionEvent.getClient().getName(), Messages.YOU_SOLD_TO_SHOP, event, MessageTarget.SELLER, "buyer", transactionEvent.getOwnerAccount().getName());
} }
if (Properties.SHOW_TRANSACTION_INFORMATION_OWNER && !Toggle.isIgnoring(event.getOwnerAccount().getUuid())) { if (Properties.SHOW_TRANSACTION_INFORMATION_OWNER && !Toggle.isIgnoring(transactionEvent.getOwnerAccount().getUuid())) {
Player owner = Bukkit.getPlayer(event.getOwnerAccount().getUuid()); Player owner = Bukkit.getPlayer(transactionEvent.getOwnerAccount().getUuid());
sendMessage(owner, event.getOwnerAccount().getName(), Messages.SOMEBODY_SOLD_TO_YOUR_SHOP, event, "seller", player.getName()); sendMessage(owner, transactionEvent.getOwnerAccount().getName(), Messages.SOMEBODY_SOLD_TO_YOUR_SHOP, event, MessageTarget.BUYER, "seller", player.getName());
} }
} }
private static void sendMessage(Player player, String playerName, Messages.Message rawMessage, TransactionEvent event, String... replacements) { private static void sendMessage(Player player, String playerName, Messages.Message rawMessage, CurrencyTransferEvent event, MessageTarget messageTarget, String... replacements) {
Location loc = event.getSign().getLocation(); TransactionEvent transactionEvent = event.getTransactionEvent();
BigDecimal actualAmount = getTransactionActualAmount(event, messageTarget);
Location loc = transactionEvent.getSign().getLocation();
Map<String, String> replacementMap = new LinkedHashMap<>(); Map<String, String> replacementMap = new LinkedHashMap<>();
replacementMap.put("price", Economy.formatBalance(event.getExactPrice())); replacementMap.put("price", Economy.formatBalance(actualAmount));
replacementMap.put("world", loc.getWorld().getName()); replacementMap.put("world", loc.getWorld().getName());
replacementMap.put("x", String.valueOf(loc.getBlockX())); replacementMap.put("x", String.valueOf(loc.getBlockX()));
replacementMap.put("y", String.valueOf(loc.getBlockY())); replacementMap.put("y", String.valueOf(loc.getBlockY()));
replacementMap.put("z", String.valueOf(loc.getBlockZ())); replacementMap.put("z", String.valueOf(loc.getBlockZ()));
replacementMap.put("material", "%item"); replacementMap.put("material", "%item");
for (int i = 0; i + 1 < replacements.length; i+=2) { for (int i = 0; i + 1 < replacements.length; i += 2) {
replacementMap.put(replacements[i], replacements[i + 1]); replacementMap.put(replacements[i], replacements[i + 1]);
} }
if (Properties.SHOWITEM_MESSAGE && MaterialUtil.Show.sendMessage(player, playerName, rawMessage, event.getStock(), replacementMap)) { if (Properties.SHOWITEM_MESSAGE && MaterialUtil.Show.sendMessage(player, playerName, rawMessage, transactionEvent.getStock(), replacementMap)) {
return; return;
} }
if (player != null) { if (player != null) {
replacementMap.put("item", ItemUtil.getItemList(event.getStock())); replacementMap.put("item", ItemUtil.getItemList(transactionEvent.getStock()));
rawMessage.sendWithPrefix(player, replacementMap); rawMessage.sendWithPrefix(player, replacementMap);
} else if (playerName != null) { } else if (playerName != null) {
replacementMap.put("item", ItemUtil.getItemList(event.getStock())); replacementMap.put("item", ItemUtil.getItemList(transactionEvent.getStock()));
ChestShop.sendBungeeMessage(playerName, rawMessage, replacementMap); ChestShop.sendBungeeMessage(playerName, rawMessage, replacementMap);
} }
} }
private static BigDecimal getTransactionActualAmount(CurrencyTransferEvent event, MessageTarget messageTarget) {
if (messageTarget == MessageTarget.SELLER) {
return event.getAmountReceived();
} else {
return event.getAmountSent();
}
}
private enum MessageTarget {
BUYER,
SELLER
}
} }

View File

@ -11,6 +11,8 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import java.util.logging.Level;
import static com.Acrobot.ChestShop.Permission.OTHER_NAME_CREATE; import static com.Acrobot.ChestShop.Permission.OTHER_NAME_CREATE;
import static com.Acrobot.ChestShop.Signs.ChestShopSign.NAME_LINE; import static com.Acrobot.ChestShop.Signs.ChestShopSign.NAME_LINE;
import static com.Acrobot.ChestShop.Events.PreShopCreationEvent.CreationOutcome.UNKNOWN_PLAYER; import static com.Acrobot.ChestShop.Events.PreShopCreationEvent.CreationOutcome.UNKNOWN_PLAYER;
@ -58,7 +60,7 @@ public class NameChecker implements Listener {
} }
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while trying to check account for name " + name + " with player " + player.getName(), e);
} }
} }
event.setOwnerAccount(account); event.setOwnerAccount(account);

View File

@ -1,6 +1,5 @@
package com.Acrobot.ChestShop.Listeners.PreShopCreation; package com.Acrobot.ChestShop.Listeners.PreShopCreation;
import com.Acrobot.Breeze.Utils.QuantityUtil;
import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Events.PreShopCreationEvent; import com.Acrobot.ChestShop.Events.PreShopCreationEvent;
import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.Acrobot.ChestShop.Signs.ChestShopSign;
@ -20,7 +19,7 @@ public class QuantityChecker implements Listener {
int amount = -1; int amount = -1;
try { try {
amount = ChestShopSign.getQuantity(event.getSignLines()); amount = ChestShopSign.getQuantity(event.getSignLines());
} catch (NumberFormatException notANumber) {} } catch (NumberFormatException ignored) {} // not a quantity on the line
if (amount < 1 || amount > Properties.MAX_SHOP_AMOUNT) { if (amount < 1 || amount > Properties.MAX_SHOP_AMOUNT) {
event.setOutcome(INVALID_QUANTITY); event.setOutcome(INVALID_QUANTITY);

View File

@ -59,7 +59,13 @@ public class PartialTransactionModule implements Listener {
return; return;
} }
event.setExactPrice(pricePerItem.multiply(new BigDecimal(amountAffordable)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP)); BigDecimal pricePerItemScaled = pricePerItem.multiply(new BigDecimal(amountAffordable)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP);
if (pricePerItem.compareTo(BigDecimal.ZERO) > 0 && pricePerItemScaled.compareTo(BigDecimal.ZERO) == 0) {
event.setCancelled(CLIENT_DOES_NOT_HAVE_ENOUGH_MONEY);
return;
}
event.setExactPrice(pricePerItemScaled);
event.setStock(getCountedItemStack(event.getStock(), amountAffordable)); event.setStock(getCountedItemStack(event.getStock(), amountAffordable));
} }
@ -72,7 +78,13 @@ public class PartialTransactionModule implements Listener {
return; return;
} }
event.setExactPrice(pricePerItem.multiply(BigDecimal.valueOf(possessedItemCount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP)); BigDecimal pricePerItemScaled = pricePerItem.multiply(new BigDecimal(possessedItemCount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP);
if (pricePerItem.compareTo(BigDecimal.ZERO) > 0 && pricePerItemScaled.compareTo(BigDecimal.ZERO) == 0) {
event.setCancelled(NOT_ENOUGH_STOCK_IN_CHEST);
return;
}
event.setExactPrice(pricePerItemScaled);
event.setStock(itemsHad); event.setStock(itemsHad);
} }
@ -84,8 +96,14 @@ public class PartialTransactionModule implements Listener {
return; return;
} }
BigDecimal pricePerItemScaled = pricePerItem.multiply(new BigDecimal(possessedItemCount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP);
if (pricePerItem.compareTo(BigDecimal.ZERO) > 0 && pricePerItemScaled.compareTo(BigDecimal.ZERO) == 0) {
event.setCancelled(NOT_ENOUGH_SPACE_IN_INVENTORY);
return;
}
event.setExactPrice(pricePerItemScaled);
event.setStock(itemsFit); event.setStock(itemsFit);
event.setExactPrice(pricePerItem.multiply(BigDecimal.valueOf(possessedItemCount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP));
} }
UUID seller = event.getOwnerAccount().getUuid(); UUID seller = event.getOwnerAccount().getUuid();
@ -126,7 +144,13 @@ public class PartialTransactionModule implements Listener {
return; return;
} }
event.setExactPrice(pricePerItem.multiply(BigDecimal.valueOf(amountAffordable)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP)); BigDecimal pricePerItemScaled = pricePerItem.multiply(new BigDecimal(amountAffordable)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP);
if (pricePerItem.compareTo(BigDecimal.ZERO) > 0 && pricePerItemScaled.compareTo(BigDecimal.ZERO) == 0) {
event.setCancelled(SHOP_DOES_NOT_HAVE_ENOUGH_MONEY);
return;
}
event.setExactPrice(pricePerItemScaled);
event.setStock(getCountedItemStack(event.getStock(), amountAffordable)); event.setStock(getCountedItemStack(event.getStock(), amountAffordable));
} }
} }
@ -140,7 +164,13 @@ public class PartialTransactionModule implements Listener {
return; return;
} }
event.setExactPrice(pricePerItem.multiply(BigDecimal.valueOf(possessedItemCount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP)); BigDecimal pricePerItemScaled = pricePerItem.multiply(new BigDecimal(possessedItemCount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP);
if (pricePerItem.compareTo(BigDecimal.ZERO) > 0 && pricePerItemScaled.compareTo(BigDecimal.ZERO) == 0) {
event.setCancelled(NOT_ENOUGH_STOCK_IN_INVENTORY);
return;
}
event.setExactPrice(pricePerItemScaled);
event.setStock(itemsHad); event.setStock(itemsHad);
} }
@ -152,8 +182,14 @@ public class PartialTransactionModule implements Listener {
return; return;
} }
BigDecimal pricePerItemScaled = pricePerItem.multiply(new BigDecimal(possessedItemCount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP);
if (pricePerItem.compareTo(BigDecimal.ZERO) > 0 && pricePerItemScaled.compareTo(BigDecimal.ZERO) == 0) {
event.setCancelled(NOT_ENOUGH_SPACE_IN_CHEST);
return;
}
event.setExactPrice(pricePerItemScaled);
event.setStock(itemsFit); event.setStock(itemsFit);
event.setExactPrice(pricePerItem.multiply(BigDecimal.valueOf(possessedItemCount)).setScale(Properties.PRICE_PRECISION, BigDecimal.ROUND_HALF_UP));
} }
CurrencyHoldEvent currencyHoldEvent = new CurrencyHoldEvent(event.getExactPrice(), client); CurrencyHoldEvent currencyHoldEvent = new CurrencyHoldEvent(event.getExactPrice(), client);
@ -175,10 +211,10 @@ public class PartialTransactionModule implements Listener {
int amount = InventoryUtil.getAmount(item, inventory); int amount = InventoryUtil.getAmount(item, inventory);
Collections.addAll(toReturn, getCountedItemStack(new ItemStack[]{item}, Collections.addAll(toReturn, getCountedItemStack(new ItemStack[]{item},
amount > item.getAmount() ? item.getAmount() : amount)); Math.min(amount, item.getAmount())));
} }
return toReturn.toArray(new ItemStack[toReturn.size()]); return toReturn.toArray(new ItemStack[0]);
} }
private static ItemStack[] getCountedItemStack(ItemStack[] stock, int numberOfItems) { private static ItemStack[] getCountedItemStack(ItemStack[] stock, int numberOfItems) {
@ -227,7 +263,7 @@ public class PartialTransactionModule implements Listener {
} }
} }
return stacks.toArray(new ItemStack[stacks.size()]); return stacks.toArray(new ItemStack[0]);
} }
/** /**
@ -247,7 +283,9 @@ public class PartialTransactionModule implements Listener {
int free = 0; int free = 0;
for (ItemStack itemInInventory : inventory.getContents()) { for (ItemStack itemInInventory : inventory.getContents()) {
if (MaterialUtil.equals(item, itemInInventory)) { if (MaterialUtil.equals(item, itemInInventory)) {
free += (maxStackSize - itemInInventory.getAmount()) % maxStackSize; if (itemInInventory != null) {
free += (maxStackSize - itemInInventory.getAmount()) % maxStackSize;
}
} }
} }
@ -271,6 +309,6 @@ public class PartialTransactionModule implements Listener {
Collections.addAll(resultStock, InventoryUtil.getItemsStacked(item)); Collections.addAll(resultStock, InventoryUtil.getItemsStacked(item));
} }
return resultStock.toArray(new ItemStack[resultStock.size()]); return resultStock.toArray(new ItemStack[0]);
} }
} }

View File

@ -17,11 +17,11 @@ public class ShopRemovalLogger implements Listener {
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public static void onShopRemoval(final ShopDestroyedEvent event) { public static void onShopRemoval(final ShopDestroyedEvent event) {
if (Properties.LOG_ALL_SHOP_REMOVALS || event.getDestroyer() != null) { if (!Properties.LOG_ALL_SHOP_REMOVALS && event.getDestroyer() != null) {
return; return;
} }
ChestShop.getBukkitServer().getScheduler().runTaskAsynchronously(ChestShop.getPlugin(), () -> { ChestShop.runInAsyncThread(() -> {
String shopOwner = ChestShopSign.getOwner(event.getSign()); String shopOwner = ChestShopSign.getOwner(event.getSign());
String typeOfShop = ChestShopSign.isAdminShop(shopOwner) ? "An Admin Shop" : "A shop belonging to " + shopOwner; String typeOfShop = ChestShopSign.isAdminShop(shopOwner) ? "An Admin Shop" : "A shop belonging to " + shopOwner;
@ -36,7 +36,7 @@ public class ShopRemovalLogger implements Listener {
prices, prices,
location); location);
ChestShop.getBukkitLogger().info(message); ChestShop.getShopLogger().info(message);
}); });
} }
} }

View File

@ -43,7 +43,7 @@ public class ItemDatabase {
itemDao = DaoCreator.getDaoAndCreateTable(Item.class); itemDao = DaoCreator.getDaoAndCreateTable(Item.class);
handleMetadataUpdate(); handleMetadataUpdate();
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while loading items database", e);
} }
} }
@ -59,7 +59,7 @@ public class ItemDatabase {
try { try {
versionConfig.save(configFile); versionConfig.save(configFile);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while updating metadata-version from " + previousVersion + " to " + newVersion, e);
} }
} else { } else {
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while updating Item Metadata database! While the plugin will still run it will work less efficiently."); ChestShop.getBukkitLogger().log(Level.WARNING, "Error while updating Item Metadata database! While the plugin will still run it will work less efficiently.");
@ -70,8 +70,10 @@ public class ItemDatabase {
private int getCurrentMetadataVersion() { private int getCurrentMetadataVersion() {
ItemStack item = new ItemStack(Material.STONE); ItemStack item = new ItemStack(Material.STONE);
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
meta.setDisplayName("GetCurrentMetadataVersion"); if (meta != null) {
item.setItemMeta(meta); meta.setDisplayName("GetCurrentMetadataVersion");
item.setItemMeta(meta);
}
Map<String, Object> serialized = item.serialize(); Map<String, Object> serialized = item.serialize();
return (int) serialized.getOrDefault("v", -1); return (int) serialized.getOrDefault("v", -1);
} }
@ -101,7 +103,7 @@ public class ItemDatabase {
item.setBase64ItemCode(Base64.encodeObject(yaml.dump(itemStack))); item.setBase64ItemCode(Base64.encodeObject(yaml.dump(itemStack)));
itemDao.update(item); itemDao.update(item);
updated.getAndIncrement(); updated.getAndIncrement();
} catch (YAMLException e) { } catch (YAMLException | ClassCastException e) {
ChestShop.getBukkitLogger().log(Level.SEVERE, "YAML of the item with ID " + Base62.encode(item.getId()) + " (" + item.getId() + ") is corrupted: \n" + serialized); ChestShop.getBukkitLogger().log(Level.SEVERE, "YAML of the item with ID " + Base62.encode(item.getId()) + " (" + item.getId() + ") is corrupted: \n" + serialized);
} }
} }
@ -117,7 +119,7 @@ public class ItemDatabase {
return true; return true;
}); });
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Unable to update metadata version of all items from " + previousVersion + " to " + newVersion, e);
return false; return false;
} finally { } finally {
it.closeQuietly(); it.closeQuietly();
@ -153,10 +155,8 @@ public class ItemDatabase {
itemDao.create(itemEntity); itemDao.create(itemEntity);
} }
return Base62.encode(itemEntity.getId()); return Base62.encode(itemEntity.getId());
} catch (SQLException e) { } catch (SQLException | IOException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Unable to get code of item " + item, e);
} catch (IOException e) {
e.printStackTrace();
} }
return null; return null;
@ -196,7 +196,7 @@ public class ItemDatabase {
return null; return null;
} }
private class YamlBukkitConstructor extends YamlConstructor { private static class YamlBukkitConstructor extends YamlConstructor {
public YamlBukkitConstructor() { public YamlBukkitConstructor() {
this.yamlConstructors.put(new Tag(Tag.PREFIX + "org.bukkit.inventory.ItemStack"), yamlConstructors.get(Tag.MAP)); this.yamlConstructors.put(new Tag(Tag.PREFIX + "org.bukkit.inventory.ItemStack"), yamlConstructors.get(Tag.MAP));
} }

View File

@ -5,7 +5,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.Locale; import java.util.Locale;
import java.util.Optional;
/** /**
* @author Acrobot * @author Acrobot
@ -43,7 +42,19 @@ public enum Permission {
NOTIFY_TOGGLE("ChestShop.toggle"), NOTIFY_TOGGLE("ChestShop.toggle"),
ACCESS_TOGGLE("ChestShop.accesstoggle"), ACCESS_TOGGLE("ChestShop.accesstoggle"),
ITEMINFO("ChestShop.iteminfo"), ITEMINFO("ChestShop.iteminfo"),
SHOPINFO("ChestShop.shopinfo"); SHOPINFO("ChestShop.shopinfo"),
NOLIMIT_MIN_BUY("ChestShop.nolimit.buy.min"),
NOLIMIT_MIN_BUY_ID("ChestShop.nolimit.buy.min."),
NOLIMIT_MAX_BUY("ChestShop.nolimit.buy.max"),
NOLIMIT_MAX_BUY_ID("ChestShop.nolimit.buy.max."),
NOLIMIT_MIN_SELL("ChestShop.nolimit.sell.min"),
NOLIMIT_MIN_SELL_ID("ChestShop.nolimit.sell.min."),
NOLIMIT_MAX_SELL("ChestShop.nolimit.sell.max"),
NOLIMIT_MAX_SELL_ID("ChestShop.nolimit.sell.max.");
private final String permission; private final String permission;

View File

@ -0,0 +1,49 @@
package com.Acrobot.ChestShop.Plugins;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Events.ItemParseEvent;
import com.Acrobot.ChestShop.Events.ItemStringQueryEvent;
import com.jojodmo.itembridge.ItemBridgeKey;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import static com.Acrobot.Breeze.Utils.StringUtil.getMinecraftStringWidth;
/**
* Support for the <a href="https://www.spigotmc.org/resources/77080/">ItemBridge plugin</a> to use their strings for
* custom items directly on ChestShop signs.
*
* @author Phoenix616
*/
public class ItemBridge implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onItemParse(ItemParseEvent event) {
if (event.getItem() == null) {
ItemStack item = com.jojodmo.itembridge.ItemBridge.getItemStack(event.getItemString());
if (item != null) {
event.setItem(item);
}
}
}
@EventHandler(priority = EventPriority.LOW)
public void onItemStringQuery(ItemStringQueryEvent event) {
ItemBridgeKey key = com.jojodmo.itembridge.ItemBridge.getItemKey(event.getItem());
// If namespace is "minecraft" then we ignore it and use our own logic
if (key != null && !"minecraft".equalsIgnoreCase(key.getNamespace())) {
String code = key.toString();
// Make sure the ItemBridge string is not too long as we can't parse shortened ones
if (event.getMaxWidth() > 0) {
int width = getMinecraftStringWidth(code);
if (width > event.getMaxWidth()) {
ChestShop.logDebug("Can't use ItemBridge alias " + code + " as it's width (" + width + ") was wider than the allowed max width of " + event.getMaxWidth());
return;
}
}
event.setItemString(key.toString());
}
}
}

View File

@ -162,8 +162,11 @@ public class LightweightChestProtection implements Listener {
return; return;
} }
Protection.Type type = Protection.Type.PRIVATE; Protection.Type type;
switch (event.getType()) { switch (event.getType()) {
case PRIVATE:
type = Protection.Type.PRIVATE;
break;
case PUBLIC: case PUBLIC:
type = Protection.Type.PUBLIC; type = Protection.Type.PUBLIC;
break; break;
@ -173,8 +176,14 @@ public class LightweightChestProtection implements Listener {
case DISPLAY: case DISPLAY:
try { try {
type = Protection.Type.valueOf("DISPLAY"); type = Protection.Type.valueOf("DISPLAY");
} catch (IllegalArgumentException ignored) {} } catch (IllegalArgumentException e) {
// Not supported
return;
}
break; break;
default:
// Not supported
return;
} }
Protection protection = null; Protection protection = null;

View File

@ -9,7 +9,9 @@ import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
/** /**
* @author Acrobot * Support RedProtect region checks.
*
* @author Phoenix616
*/ */
public class RedProtectBuilding implements Listener { public class RedProtectBuilding implements Listener {
private RedProtect redProtect; private RedProtect redProtect;

View File

@ -10,7 +10,6 @@ import com.Acrobot.ChestShop.Events.AccountQueryEvent;
import com.Acrobot.ChestShop.Permission; import com.Acrobot.ChestShop.Permission;
import com.Acrobot.ChestShop.UUIDs.NameManager; import com.Acrobot.ChestShop.UUIDs.NameManager;
import com.Acrobot.ChestShop.Utils.uBlock; import com.Acrobot.ChestShop.Utils.uBlock;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
@ -22,6 +21,7 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import java.util.Locale; import java.util.Locale;
import java.util.Optional;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -129,6 +129,16 @@ public class ChestShopSign {
return false; return false;
} }
public static Block getShopBlock(InventoryHolder holder) {
if (holder instanceof DoubleChest) {
return Optional.ofNullable(getShopBlock(((DoubleChest) holder).getLeftSide()))
.orElse(getShopBlock(((DoubleChest) holder).getRightSide()));
} else if (holder instanceof BlockState) {
return ((BlockState) holder).getBlock();
}
return null;
}
public static boolean canAccess(Player player, Sign sign) { public static boolean canAccess(Player player, Sign sign) {
return hasPermission(player, Permission.OTHER_NAME_ACCESS, sign); return hasPermission(player, Permission.OTHER_NAME_ACCESS, sign);
} }
@ -183,7 +193,7 @@ public class ChestShopSign {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
boolean matches = false; boolean matches = false;
for (Pattern pattern : SHOP_SIGN_PATTERN[i]) { for (Pattern pattern : SHOP_SIGN_PATTERN[i]) {
if (pattern.matcher(StringUtils.strip(StringUtil.stripColourCodes(lines[i+1]))).matches()) { if (pattern.matcher(StringUtil.strip(StringUtil.stripColourCodes(lines[i+1]))).matches()) {
matches = true; matches = true;
break; break;
} }
@ -213,7 +223,7 @@ public class ChestShopSign {
* @return The owner string * @return The owner string
*/ */
public static String getOwner(String[] lines) { public static String getOwner(String[] lines) {
return StringUtil.stripColourCodes(StringUtils.strip(StringUtil.stripColourCodes(lines[NAME_LINE]))); return StringUtil.stripColourCodes(StringUtil.strip(StringUtil.stripColourCodes(lines[NAME_LINE])));
} }
/** /**
@ -233,7 +243,7 @@ public class ChestShopSign {
* @throws IllegalArgumentException Thrown when an invalid quantity is present * @throws IllegalArgumentException Thrown when an invalid quantity is present
*/ */
public static String getQuantityLine(String[] lines) throws IllegalArgumentException { public static String getQuantityLine(String[] lines) throws IllegalArgumentException {
return lines.length > QUANTITY_LINE ? StringUtils.strip(StringUtil.stripColourCodes(lines[QUANTITY_LINE])) : ""; return lines.length > QUANTITY_LINE ? StringUtil.strip(StringUtil.stripColourCodes(lines[QUANTITY_LINE])) : "";
} }
/** /**
@ -262,7 +272,7 @@ public class ChestShopSign {
* @return The price line * @return The price line
*/ */
public static String getPrice(Sign sign) { public static String getPrice(Sign sign) {
return StringUtils.strip(StringUtil.stripColourCodes(sign.getLine(PRICE_LINE))); return StringUtil.strip(StringUtil.stripColourCodes(sign.getLine(PRICE_LINE)));
} }
/** /**
@ -271,7 +281,7 @@ public class ChestShopSign {
* @return The price line * @return The price line
*/ */
public static String getPrice(String[] lines) { public static String getPrice(String[] lines) {
return lines.length > PRICE_LINE ? StringUtils.strip(StringUtil.stripColourCodes(lines[PRICE_LINE])) : ""; return lines.length > PRICE_LINE ? StringUtil.strip(StringUtil.stripColourCodes(lines[PRICE_LINE])) : "";
} }
/** /**
@ -289,6 +299,6 @@ public class ChestShopSign {
* @return The item line * @return The item line
*/ */
public static String getItem(String[] lines) { public static String getItem(String[] lines) {
return lines.length > ITEM_LINE ? StringUtils.strip(StringUtil.stripColourCodes(lines[ITEM_LINE])) : ""; return lines.length > ITEM_LINE ? StringUtil.strip(StringUtil.stripColourCodes(lines[ITEM_LINE])) : "";
} }
} }

View File

@ -12,10 +12,11 @@ import com.Acrobot.ChestShop.Events.AccountAccessEvent;
import com.Acrobot.ChestShop.Events.AccountQueryEvent; import com.Acrobot.ChestShop.Events.AccountQueryEvent;
import com.Acrobot.ChestShop.Permission; import com.Acrobot.ChestShop.Permission;
import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.Acrobot.ChestShop.Signs.ChestShopSign;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.SelectArg; import com.j256.ormlite.stmt.SelectArg;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -36,6 +37,8 @@ import java.util.logging.Level;
*/ */
@SuppressWarnings("UnusedAssignment") // I deliberately set the variables to null while initializing @SuppressWarnings("UnusedAssignment") // I deliberately set the variables to null while initializing
public class NameManager implements Listener { public class NameManager implements Listener {
private static final Object accountsLock = new Object();
private static Dao<Account, String> accounts; private static Dao<Account, String> accounts;
private static SimpleCache<String, Account> usernameToAccount = new SimpleCache<>(Properties.CACHE_SIZE); private static SimpleCache<String, Account> usernameToAccount = new SimpleCache<>(Properties.CACHE_SIZE);
@ -63,8 +66,8 @@ public class NameManager implements Listener {
* @throws IllegalArgumentException when an invalid player object was passed * @throws IllegalArgumentException when an invalid player object was passed
*/ */
public static Account getOrCreateAccount(OfflinePlayer player) { public static Account getOrCreateAccount(OfflinePlayer player) {
Validate.notNull(player.getName(), "Name of player " + player.getUniqueId() + " is null?"); Preconditions.checkNotNull(player.getName(), "Name of player " + player.getUniqueId() + " is null?");
Validate.isTrue(!(player instanceof Player) || !Properties.ENSURE_CORRECT_PLAYERID || uuidVersion < 0 || player.getUniqueId().version() == uuidVersion, Preconditions.checkArgument(player instanceof Player || !Properties.ENSURE_CORRECT_PLAYERID || uuidVersion < 0 || player.getUniqueId().version() == uuidVersion,
"Invalid OfflinePlayer! " + player.getUniqueId() + " has version " + player.getUniqueId().version() + " and not server version " + uuidVersion + ". " + "Invalid OfflinePlayer! " + player.getUniqueId() + " has version " + player.getUniqueId().version() + " and not server version " + uuidVersion + ". " +
"If you believe that is an error and your setup allows such UUIDs then set the ENSURE_CORRECT_PLAYERID config option to false."); "If you believe that is an error and your setup allows such UUIDs then set the ENSURE_CORRECT_PLAYERID config option to false.");
return getOrCreateAccount(player.getUniqueId(), player.getName()); return getOrCreateAccount(player.getUniqueId(), player.getName());
@ -79,8 +82,8 @@ public class NameManager implements Listener {
* @throws IllegalArgumentException when id or name are null * @throws IllegalArgumentException when id or name are null
*/ */
public static Account getOrCreateAccount(UUID id, String name) { public static Account getOrCreateAccount(UUID id, String name) {
Validate.notNull(id, "UUID of player is null?"); Preconditions.checkNotNull(id, "UUID of player is null?");
Validate.notNull(name, "Name of player " + id + " is null?"); Preconditions.checkNotNull(name, "Name of player " + id + " is null?");
Account account = getAccount(id); Account account = getAccount(id);
if (account == null) { if (account == null) {
@ -97,20 +100,22 @@ public class NameManager implements Listener {
*/ */
public static Account getAccount(UUID uuid) { public static Account getAccount(UUID uuid) {
try { try {
return uuidToAccount.get(uuid, () -> { synchronized (accountsLock) {
try { return uuidToAccount.get(uuid, () -> {
Account account = accounts.queryBuilder().orderBy("lastSeen", false).where().eq("uuid", new SelectArg(uuid)).queryForFirst(); try {
if (account != null) { Account account = accounts.queryBuilder().orderBy("lastSeen", false).where().eq("uuid", new SelectArg(uuid)).queryForFirst();
account.setUuid(uuid); // HOW IS IT EVEN POSSIBLE THAT UUID IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?! if (account != null) {
shortToAccount.put(account.getShortName(), account); account.setUuid(uuid); // HOW IS IT EVEN POSSIBLE THAT UUID IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
usernameToAccount.put(account.getName(), account); shortToAccount.put(account.getShortName(), account);
return account; usernameToAccount.put(account.getName(), account);
return account;
}
} catch (SQLException e) {
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + uuid + ":", e);
} }
} catch (SQLException e) { throw new Exception("Could not find account for " + uuid);
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + uuid + ":", e); });
} }
throw new Exception("Could not find account for " + uuid);
});
} catch (ExecutionException ignored) { } catch (ExecutionException ignored) {
return null; return null;
} }
@ -124,21 +129,24 @@ public class NameManager implements Listener {
* @throws IllegalArgumentException if the username is empty or null * @throws IllegalArgumentException if the username is empty or null
*/ */
public static Account getAccount(String fullName) { public static Account getAccount(String fullName) {
Validate.notEmpty(fullName, "fullName cannot be null or empty!"); Preconditions.checkNotNull(fullName, "fullName cannot be null!");
Preconditions.checkArgument(!fullName.isEmpty(), "fullName cannot be empty!");
try { try {
return usernameToAccount.get(fullName, () -> { synchronized (accountsLock) {
try { return usernameToAccount.get(fullName, () -> {
Account account = accounts.queryBuilder().orderBy("lastSeen", false).where().eq("name", new SelectArg(fullName)).queryForFirst(); try {
if (account != null) { Account account = accounts.queryBuilder().orderBy("lastSeen", false).where().eq("name", new SelectArg(fullName)).queryForFirst();
account.setName(fullName); // HOW IS IT EVEN POSSIBLE THAT THE NAME IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?! if (account != null) {
shortToAccount.put(account.getShortName(), account); account.setName(fullName); // HOW IS IT EVEN POSSIBLE THAT THE NAME IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
return account; shortToAccount.put(account.getShortName(), account);
return account;
}
} catch (SQLException e) {
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + fullName + ":", e);
} }
} catch (SQLException e) { throw new Exception("Could not find account for " + fullName);
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + fullName + ":", e); });
} }
throw new Exception("Could not find account for " + fullName);
});
} catch (ExecutionException ignored) { } catch (ExecutionException ignored) {
return null; return null;
} }
@ -161,22 +169,25 @@ public class NameManager implements Listener {
*/ */
@Deprecated @Deprecated
public static Account getAccountFromShortName(String shortName) { public static Account getAccountFromShortName(String shortName) {
Validate.notEmpty(shortName, "shortName cannot be null or empty!"); Preconditions.checkNotNull(shortName, "shortName cannot be null!");
Preconditions.checkArgument(!shortName.isEmpty(), "shortName cannot be empty!");
Account account = null; Account account = null;
try { try {
account = shortToAccount.get(shortName, () -> { synchronized (accountsLock) {
try { account = shortToAccount.get(shortName, () -> {
Account a = accounts.queryBuilder().where().eq("shortName", new SelectArg(shortName)).queryForFirst(); try {
if (a != null) { Account a = accounts.queryBuilder().where().eq("shortName", new SelectArg(shortName)).queryForFirst();
a.setShortName(shortName); // HOW IS IT EVEN POSSIBLE THAT THE NAME IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?! if (a != null) {
return a; a.setShortName(shortName); // HOW IS IT EVEN POSSIBLE THAT THE NAME IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
return a;
}
} catch (SQLException e) {
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + shortName + ":", e);
} }
} catch (SQLException e) { throw new Exception("Could not find account for " + shortName);
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + shortName + ":", e); });
} }
throw new Exception("Could not find account for " + shortName);
});
} catch (ExecutionException ignored) {} } catch (ExecutionException ignored) {}
return account; return account;
} }
@ -220,27 +231,29 @@ public class NameManager implements Listener {
final UUID uuid = player.getUniqueId(); final UUID uuid = player.getUniqueId();
Account latestAccount = null; Account latestAccount = null;
try { synchronized (accountsLock) {
latestAccount = accounts.queryBuilder().where().eq("uuid", new SelectArg(uuid)).and().eq("name", new SelectArg(player.getName())).queryForFirst(); try {
} catch (SQLException e) { latestAccount = accounts.queryBuilder().where().eq("uuid", new SelectArg(uuid)).and().eq("name", new SelectArg(player.getName())).queryForFirst();
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while searching for latest account of " + player.getName() + "/" + uuid + ":", e); } catch (SQLException e) {
} ChestShop.getBukkitLogger().log(Level.WARNING, "Error while searching for latest account of " + player.getName() + "/" + uuid + ":", e);
}
if (latestAccount == null) { if (latestAccount == null) {
latestAccount = new Account(player.getName(), getNewShortenedName(player), player.getUniqueId()); latestAccount = new Account(player.getName(), getNewShortenedName(player), player.getUniqueId());
} }
latestAccount.setLastSeen(new Date()); latestAccount.setLastSeen(new Date());
try { try {
accounts.createOrUpdate(latestAccount); accounts.createOrUpdate(latestAccount);
} catch (SQLException e) { } catch (SQLException e) {
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while updating account " + latestAccount + ":", e); ChestShop.getBukkitLogger().log(Level.WARNING, "Error while updating account " + latestAccount + ":", e);
return null; return null;
} }
usernameToAccount.put(latestAccount.getName(), latestAccount); usernameToAccount.put(latestAccount.getName(), latestAccount);
uuidToAccount.put(uuid, latestAccount); uuidToAccount.put(uuid, latestAccount);
shortToAccount.put(latestAccount.getShortName(), latestAccount); shortToAccount.put(latestAccount.getShortName(), latestAccount);
}
return latestAccount; return latestAccount;
} }
@ -269,7 +282,12 @@ public class NameManager implements Listener {
public static boolean canUseName(Player player, Permission base, String name) { public static boolean canUseName(Player player, Permission base, String name) {
if (ChestShopSign.isAdminShop(name)) { if (ChestShopSign.isAdminShop(name)) {
return Permission.has(player, Permission.ADMIN_SHOP); if (Permission.has(player, Permission.ADMIN_SHOP)) {
return true;
} else {
ChestShop.logDebug(player.getName() + " cannot use the name " + name + " as it's an admin shop and they don't have the permission " + Permission.ADMIN_SHOP);
return false;
}
} }
if (Permission.otherName(player, base, name)) { if (Permission.otherName(player, base, name)) {
@ -280,6 +298,12 @@ public class NameManager implements Listener {
ChestShop.callEvent(queryEvent); ChestShop.callEvent(queryEvent);
Account account = queryEvent.getAccount(); Account account = queryEvent.getAccount();
if (account == null) { if (account == null) {
// There is no account by the provided name, but it matches the player name
// Return true as they specified their own name and a new account should get created
if (player.getName().equalsIgnoreCase(name)) {
return true;
}
ChestShop.logDebug(player.getName() + " cannot use the name " + name + " for a shop as no account with that name exists");
return false; return false;
} }
if (!account.getName().equalsIgnoreCase(name) && Permission.otherName(player, base, account.getName())) { if (!account.getName().equalsIgnoreCase(name) && Permission.otherName(player, base, account.getName())) {
@ -294,6 +318,11 @@ public class NameManager implements Listener {
public static void onAccountAccessCheck(AccountAccessEvent event) { public static void onAccountAccessCheck(AccountAccessEvent event) {
if (!event.canAccess()) { if (!event.canAccess()) {
event.setAccess(event.getPlayer().getUniqueId().equals(event.getAccount().getUuid())); event.setAccess(event.getPlayer().getUniqueId().equals(event.getAccount().getUuid()));
if (!event.canAccess()) {
ChestShop.logDebug(event.getPlayer().getName() + "/" + event.getPlayer().getUniqueId()
+ " cannot access the account " + event.getAccount().getName() + "/" + event.getAccount().getUuid()
+ " as their UUID doesn't match!");
}
} }
} }
@ -316,7 +345,14 @@ public class NameManager implements Listener {
try { try {
accounts = DaoCreator.getDaoAndCreateTable(Account.class); accounts = DaoCreator.getDaoAndCreateTable(Account.class);
adminAccount = new Account(Properties.ADMIN_SHOP_NAME, Bukkit.getOfflinePlayer(Properties.ADMIN_SHOP_NAME).getUniqueId()); try {
adminAccount = new Account(Properties.ADMIN_SHOP_NAME, Bukkit.getOfflinePlayer(Properties.ADMIN_SHOP_NAME).getUniqueId());
} catch (NullPointerException ratelimitedException) {
// This happens when the server was ratelimited by Mojang. Unfortunately there is no nice way to check that.
// We fall back to the method used by CraftBukkit to generate an OfflinePlayer's UUID
adminAccount = new Account(Properties.ADMIN_SHOP_NAME, UUID.nameUUIDFromBytes(("OfflinePlayer:" + Properties.ADMIN_SHOP_NAME).getBytes(Charsets.UTF_8)));
ChestShop.getBukkitLogger().log(Level.WARNING, "Your server appears to be ratelimited by Mojang and can't query UUID data from their API. If you run into issues with admin shops please report them!");
}
accounts.createOrUpdate(adminAccount); accounts.createOrUpdate(adminAccount);
if (!Properties.SERVER_ECONOMY_ACCOUNT.isEmpty()) { if (!Properties.SERVER_ECONOMY_ACCOUNT.isEmpty()) {
@ -337,7 +373,7 @@ public class NameManager implements Listener {
} }
} }
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while trying to setup accounts", e);
} }
} }

View File

@ -1,5 +1,6 @@
package com.Acrobot.ChestShop.Updater; package com.Acrobot.ChestShop.Updater;
import com.Acrobot.ChestShop.ChestShop;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
@ -55,7 +56,7 @@ public class JenkinsBuildsNotifier implements Runnable {
try { try {
apiUrl = new URL(jenkinsJobUrl + "api/json"); apiUrl = new URL(jenkinsJobUrl + "api/json");
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, this); ChestShop.runInAsyncThread(this);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
plugin.getLogger().log(Level.WARNING, "Can not check for new dev builds as " + jenkinsJobUrl + "api/json is not a valid url!", e); plugin.getLogger().log(Level.WARNING, "Can not check for new dev builds as " + jenkinsJobUrl + "api/json is not a valid url!", e);
} }

View File

@ -6,6 +6,8 @@
package com.Acrobot.ChestShop.Updater; package com.Acrobot.ChestShop.Updater;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
@ -19,6 +21,8 @@ import java.net.URLConnection;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
@ -39,12 +43,13 @@ import java.util.zip.ZipFile;
* @version 2.1 * @version 2.1
*/ */
public class Updater { public final class Updater {
private Plugin plugin; private Plugin plugin;
private UpdateType type; private UpdateType type;
private String versionName; private String versionName;
private String versionLink; private String versionLink;
private String versionHash;
private String versionType; private String versionType;
private String versionGameVersion; private String versionGameVersion;
@ -54,21 +59,21 @@ public class Updater {
private File file; // The plugin's file private File file; // The plugin's file
private Thread thread; // Updater thread private Thread thread; // Updater thread
private int id = -1; // Project's Curse ID private String id; // Project's Curse ID
private String apiKey = null; // BukkitDev ServerMods API key private String apiKey = null; // Modrinth API key
private static final String TITLE_VALUE = "name"; // Gets remote file's title private static final String TITLE_VALUE = "version_number"; // Gets remote version
private static final String LINK_VALUE = "downloadUrl"; // Gets remote file's download link private static final String FILES_VALUE = "files"; // Gets all files associated with that version
private static final String TYPE_VALUE = "releaseType"; // Gets remote file's release type private static final String LINK_VALUE = "url"; // Gets remote file's download link
private static final String VERSION_VALUE = "gameVersion"; // Gets remote file's build version private static final String TYPE_VALUE = "version_type"; // Gets remote file's release type
private static final String QUERY = "/servermods/files?projectIds="; // Path to GET private static final String VERSION_VALUE = "game_versions"; // Gets remote file's build version
private static final String HOST = "https://api.curseforge.com"; // Slugs will be appended to this to get to the project's RSS feed private static final String QUERY = "/v2/project/%projectid%/version"; // Path to GET
private static final String HOST = "https://api.modrinth.com"; // Slugs will be appended to this to get to the project's versions
private static final String USER_AGENT = "Updater (by Gravity)"; private static final String USER_AGENT = "Updater v2.1 (by Gravity) - Modified by Phoenix616 for Modrinth";
private static final String delimiter = "^v|[\\s_-]v"; // Used for locating version numbers in file names private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+\\.\\d+(?>\\.\\d+)?)"); // Used for locating version numbers in file names
private static final String[] NO_UPDATE_TAG = { "-DEV", "-PRE", "-SNAPSHOT" }; // If the version number contains one of these, don't update. private static final String[] NO_UPDATE_TAG = { "-DEV", "-PRE", "-SNAPSHOT" }; // If the version number contains one of these, don't update.
private static final int BYTE_SIZE = 1024; // Used for downloading files private static final int BYTE_SIZE = 1024; // Used for downloading files
private final YamlConfiguration config = new YamlConfiguration(); // Config file private final YamlConfiguration config = new YamlConfiguration(); // Config file
private String updateFolder;// The folder that downloads will be placed in
private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS; // Used for determining the outcome of the update process private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS; // Used for determining the outcome of the update process
/** /**
@ -110,7 +115,11 @@ public class Updater {
/** /**
* The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded. * The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded.
*/ */
UPDATE_AVAILABLE UPDATE_AVAILABLE,
/**
* The downloaded file does not match the SHA1 hash sum provided by the api.
*/
FAIL_HASH,
} }
/** /**
@ -153,27 +162,26 @@ public class Updater {
* Initialize the updater. * Initialize the updater.
* *
* @param plugin The plugin that is checking for an update. * @param plugin The plugin that is checking for an update.
* @param id The dev.bukkit.org id of the project. * @param id The id of the project.
* @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class. * @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class.
* @param type Specify the type of update this will be. See {@link UpdateType} * @param type Specify the type of update this will be. See {@link UpdateType}
* @param announce True if the program should announce the progress of new updates in console. * @param announce True if the program should announce the progress of new updates in console.
*/ */
public Updater(Plugin plugin, int id, File file, UpdateType type, boolean announce) { public Updater(Plugin plugin, String id, File file, UpdateType type, boolean announce) {
this.plugin = plugin; this.plugin = plugin;
this.type = type; this.type = type;
this.announce = announce; this.announce = announce;
this.file = file; this.file = file;
this.id = id; this.id = id;
this.updateFolder = plugin.getServer().getUpdateFolder();
final File pluginFile = plugin.getDataFolder().getParentFile(); final File pluginFile = plugin.getDataFolder().getParentFile();
final File updaterFile = new File(pluginFile, "Updater"); final File updaterFile = new File(pluginFile, "Updater");
final File updaterConfigFile = new File(updaterFile, "config.yml"); final File updaterConfigFile = new File(updaterFile, "config.yml");
this.config.options().header("This configuration file affects all plugins using the Updater system (version 2+ - http://forums.bukkit.org/threads/96681/ )" + '\n' this.config.options().header("This configuration file affects all plugins using the Updater system (version 2+ )" + '\n'
+ "If you wish to use your API key, read http://wiki.bukkit.org/ServerMods_API and place it below." + '\n' + "If you wish to use your API key, then you can get it from https://modrinth.com/settings/pats and place it below." + '\n'
+ "Some updating systems will not adhere to the disabled value, but these may be turned off in their plugin's configuration."); + "Some updating systems will not adhere to the disabled value, but these may be turned off in their plugin's configuration.");
this.config.addDefault("api-key", "PUT_API_KEY_HERE"); this.config.addDefault("modrinth-key", "PUT_PAT_HERE");
this.config.addDefault("disable", false); this.config.addDefault("disable", false);
if (!updaterFile.exists()) { if (!updaterFile.exists()) {
@ -204,14 +212,14 @@ public class Updater {
} }
String key = this.config.getString("api-key"); String key = this.config.getString("api-key");
if (key.equalsIgnoreCase("PUT_API_KEY_HERE") || key.equals("")) { if (key != null && (key.equalsIgnoreCase("PUT_API_KEY_HERE") || key.equals(""))) {
key = null; key = null;
} }
this.apiKey = key; this.apiKey = key;
try { try {
this.url = new URL(Updater.HOST + Updater.QUERY + id); this.url = new URL(Updater.HOST + Updater.QUERY.replace("%projectid%", id));
} catch (final MalformedURLException e) { } catch (final MalformedURLException e) {
plugin.getLogger().log(Level.SEVERE, "The project ID provided for updating, " + id + " is invalid.", e); plugin.getLogger().log(Level.SEVERE, "The project ID provided for updating, " + id + " is invalid.", e);
this.result = UpdateResult.FAIL_BADID; this.result = UpdateResult.FAIL_BADID;
@ -240,6 +248,16 @@ public class Updater {
*/ */
public ReleaseType getLatestType() { public ReleaseType getLatestType() {
this.waitForThread(); this.waitForThread();
return getLatestTypeInternal();
}
/**
* Get the latest version's release type without waiting for the thread to finish.
*
* @return latest version's release type.
* @see ReleaseType
*/
private ReleaseType getLatestTypeInternal() {
if (this.versionType != null) { if (this.versionType != null) {
for (ReleaseType type : ReleaseType.values()) { for (ReleaseType type : ReleaseType.values()) {
if (this.versionType.equals(type.name().toLowerCase(Locale.ROOT))) { if (this.versionType.equals(type.name().toLowerCase(Locale.ROOT))) {
@ -305,57 +323,61 @@ public class Updater {
if (!folder.exists()) { if (!folder.exists()) {
folder.mkdir(); folder.mkdir();
} }
BufferedInputStream in = null;
FileOutputStream fout = null;
try { try {
// Download the file // Download the file
final URL url = new URL(link); final URL url = new URL(link);
final int fileLength = url.openConnection().getContentLength(); final int fileLength = url.openConnection().getContentLength();
in = new BufferedInputStream(url.openStream()); final File targetFile = new File(folder, file);
fout = new FileOutputStream(folder.getAbsolutePath() + File.separator + file); try (BufferedInputStream in = new BufferedInputStream(url.openStream());
FileOutputStream fout = new FileOutputStream(targetFile)) {
final byte[] data = new byte[Updater.BYTE_SIZE]; final byte[] data = new byte[Updater.BYTE_SIZE];
int count; int count;
if (this.announce) { if (this.announce) {
this.plugin.getLogger().info("About to download a new update: " + this.versionName); this.plugin.getLogger().info("About to download a new update: " + this.versionName);
}
long downloaded = 0;
while ((count = in.read(data, 0, Updater.BYTE_SIZE)) != -1) {
downloaded += count;
fout.write(data, 0, count);
final int percent = (int) ((downloaded * 100) / fileLength);
if (this.announce && ((percent % 10) == 0)) {
this.plugin.getLogger().info("Downloading update: " + percent + "% of " + fileLength + " bytes.");
} }
} long downloaded = 0;
//Just a quick check to make sure we didn't leave any files from last time... int lastAnnouncePercent = 0;
for (final File xFile : new File(this.plugin.getDataFolder().getParent(), this.updateFolder).listFiles()) { while ((count = in.read(data, 0, Updater.BYTE_SIZE)) != -1) {
if (xFile.getName().endsWith(".zip")) { downloaded += count;
xFile.delete(); fout.write(data, 0, count);
final int percent = (int) ((downloaded * 100) / fileLength);
if (this.announce && lastAnnouncePercent != percent && ((percent % 10) == 0)) {
lastAnnouncePercent = percent;
this.plugin.getLogger().info("Downloading update: " + percent + "% of " + fileLength + " bytes.");
}
}
// Check sha1 sum of the downloaded file
if (this.versionHash != null) {
final String fileHash = Files.asByteSource(targetFile).hash(Hashing.sha512()).toString();
if (!this.versionHash.equalsIgnoreCase(fileHash)) {
this.plugin.getLogger().warning("Downloaded file " + file + " does not match the remote file's SHA-1 hash");
this.result = UpdateResult.FAIL_HASH;
return;
}
}
//Just a quick check to make sure we didn't leave any files from last time...
File[] files = this.plugin.getServer().getUpdateFolderFile().listFiles();
if (files != null) {
for (final File xFile : files) {
if (xFile.getName().endsWith(".zip")) {
xFile.delete();
}
}
}
// Check to see if it's a zip file, if it is, unzip it.
final File dFile = new File(folder.getAbsolutePath(), file);
if (dFile.getName().endsWith(".zip")) {
// Unzip
this.unzip(dFile.getCanonicalPath());
}
if (this.announce) {
this.plugin.getLogger().info("Finished updating.");
} }
}
// Check to see if it's a zip file, if it is, unzip it.
final File dFile = new File(folder.getAbsolutePath() + File.separator + file);
if (dFile.getName().endsWith(".zip")) {
// Unzip
this.unzip(dFile.getCanonicalPath());
}
if (this.announce) {
this.plugin.getLogger().info("Finished updating.");
} }
} catch (final Exception ex) { } catch (final Exception ex) {
this.plugin.getLogger().warning("The auto-updater tried to download a new update, but was unsuccessful."); this.plugin.getLogger().warning("The auto-updater tried to download a new update, but was unsuccessful.");
this.result = Updater.UpdateResult.FAIL_DOWNLOAD; this.result = Updater.UpdateResult.FAIL_DOWNLOAD;
} finally {
try {
if (in != null) {
in.close();
}
if (fout != null) {
fout.close();
}
} catch (final Exception ex) {
}
} }
} }
@ -365,21 +387,18 @@ public class Updater {
* @param file the location of the file to extract. * @param file the location of the file to extract.
*/ */
private void unzip(String file) { private void unzip(String file) {
try { final File fSourceZip = new File(file);
final File fSourceZip = new File(file); final String zipPath = file.substring(0, file.length() - 4);
final String zipPath = file.substring(0, file.length() - 4); try (ZipFile zipFile = new ZipFile(fSourceZip)) {
ZipFile zipFile = new ZipFile(fSourceZip);
Enumeration<? extends ZipEntry> e = zipFile.entries(); Enumeration<? extends ZipEntry> e = zipFile.entries();
while (e.hasMoreElements()) { while (e.hasMoreElements()) {
ZipEntry entry = e.nextElement(); ZipEntry entry = e.nextElement();
File destinationFilePath = new File(zipPath, entry.getName()); File destinationFilePath = new File(zipPath, entry.getName());
destinationFilePath.getParentFile().mkdirs(); destinationFilePath.getParentFile().mkdirs();
if (entry.isDirectory()) { if (!entry.isDirectory()) {
continue;
} else {
final BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry)); final BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
int b; int b;
final byte buffer[] = new byte[Updater.BYTE_SIZE]; final byte[] buffer = new byte[Updater.BYTE_SIZE];
final FileOutputStream fos = new FileOutputStream(destinationFilePath); final FileOutputStream fos = new FileOutputStream(destinationFilePath);
final BufferedOutputStream bos = new BufferedOutputStream(fos, Updater.BYTE_SIZE); final BufferedOutputStream bos = new BufferedOutputStream(fos, Updater.BYTE_SIZE);
while ((b = bis.read(buffer, 0, Updater.BYTE_SIZE)) != -1) { while ((b = bis.read(buffer, 0, Updater.BYTE_SIZE)) != -1) {
@ -390,43 +409,47 @@ public class Updater {
bis.close(); bis.close();
final String name = destinationFilePath.getName(); final String name = destinationFilePath.getName();
if (name.endsWith(".jar") && this.pluginFile(name)) { if (name.endsWith(".jar") && this.pluginFile(name)) {
destinationFilePath.renameTo(new File(this.plugin.getDataFolder().getParent(), this.updateFolder + File.separator + name)); destinationFilePath.renameTo(new File(this.plugin.getServer().getUpdateFolderFile(),name));
} }
} }
entry = null;
destinationFilePath = null;
} }
e = null;
zipFile.close(); zipFile.close();
zipFile = null;
// Move any plugin data folders that were included to the right place, Bukkit won't do this for us. // Move any plugin data folders that were included to the right place, Bukkit won't do this for us.
for (final File dFile : new File(zipPath).listFiles()) { File[] files = new File(zipPath).listFiles();
if (dFile.isDirectory()) { if (files != null) {
if (this.pluginFile(dFile.getName())) { for (final File dFile : files) {
final File oFile = new File(this.plugin.getDataFolder().getParent(), dFile.getName()); // Get current dir if (dFile.isDirectory()) {
final File[] contents = oFile.listFiles(); // List of existing files in the current dir if (this.pluginFile(dFile.getName())) {
for (final File cFile : dFile.listFiles()) // Loop through all the files in the new dir final File oFile = new File(this.plugin.getDataFolder().getParent(), dFile.getName()); // Get current dir
{ final File[] contents = oFile.listFiles(); // List of existing files in the current dir
boolean found = false; final File[] newFiles = dFile.listFiles();
for (final File xFile : contents) // Loop through contents to see if it exists if (newFiles != null) {
{ for (final File cFile : newFiles) // Loop through all the files in the new dir
if (xFile.getName().equals(cFile.getName())) { {
found = true; boolean found = false;
break; if (contents != null) {
for (final File xFile : contents) // Loop through contents to see if it exists
{
if (xFile.getName().equals(cFile.getName())) {
found = true;
break;
}
}
}
if (!found) {
// Move the new file into the current dir
cFile.renameTo(new File(oFile.getCanonicalFile(), cFile.getName()));
} else {
// This file already exists, so we don't need it anymore.
cFile.delete();
}
} }
} }
if (!found) {
// Move the new file into the current dir
cFile.renameTo(new File(oFile.getCanonicalFile() + File.separator + cFile.getName()));
} else {
// This file already exists, so we don't need it anymore.
cFile.delete();
}
} }
} }
dFile.delete();
} }
dFile.delete();
} }
new File(zipPath).delete(); new File(zipPath).delete();
fSourceZip.delete(); fSourceZip.delete();
@ -444,9 +467,12 @@ public class Updater {
* @return true if a file inside the plugins folder is named this. * @return true if a file inside the plugins folder is named this.
*/ */
private boolean pluginFile(String name) { private boolean pluginFile(String name) {
for (final File file : new File("plugins").listFiles()) { File[] files = new File("plugins").listFiles();
if (file.getName().equals(name)) { if (files != null) {
return true; for (final File file : files) {
if (file.getName().equals(name)) {
return true;
}
} }
} }
return false; return false;
@ -460,11 +486,14 @@ public class Updater {
*/ */
private boolean versionCheck(String title) { private boolean versionCheck(String title) {
if (this.type != UpdateType.NO_VERSION_CHECK) { if (this.type != UpdateType.NO_VERSION_CHECK) {
final String localVersion = this.plugin.getDescription().getVersion(); final String rawLocalVersion = this.plugin.getDescription().getVersion();
if (title.split(delimiter).length == 2) { final Matcher localMatcher = Updater.VERSION_PATTERN.matcher(rawLocalVersion);
final String remoteVersion = title.split(delimiter)[1].split(" ")[0]; // Get the newest file's version number final Matcher titleMatcher = Updater.VERSION_PATTERN.matcher(title);
if (titleMatcher.find() && localMatcher.find()) {
final String localVersion = localMatcher.group(1); // Get the plugins version number
final String remoteVersion = titleMatcher.group(1); // Get the newest file's version number
if (this.hasTag(localVersion) || !this.shouldUpdate(localVersion, remoteVersion)) { if (this.hasTag(rawLocalVersion) || !this.shouldUpdate(localVersion, remoteVersion)) {
// We already have the latest version, or this build is tagged for no-update // We already have the latest version, or this build is tagged for no-update
this.result = Updater.UpdateResult.NO_UPDATE; this.result = Updater.UpdateResult.NO_UPDATE;
return false; return false;
@ -506,11 +535,49 @@ public class Updater {
* @return true if Updater should consider the remote version an update, false if not. * @return true if Updater should consider the remote version an update, false if not.
*/ */
public boolean shouldUpdate(String localVersion, String remoteVersion) { public boolean shouldUpdate(String localVersion, String remoteVersion) {
if (localVersion.contains("DEV") || getLatestType() != ReleaseType.RELEASE) { if (this.type != Updater.UpdateType.NO_DOWNLOAD && localVersion.contains("DEV") || getLatestTypeInternal() != ReleaseType.RELEASE) {
return false; //Do not download alphas or betas return false; //Do not download alphas or betas
} }
return !localVersion.equalsIgnoreCase(remoteVersion); if (localVersion.equalsIgnoreCase(remoteVersion)) {
return true; //Already the same version
}
try {
int[] localSemanticVersion = parseVersion(localVersion);
int[] remoteSemanticVersion = parseVersion(remoteVersion);
for (int i = 0; i < localSemanticVersion.length; i++) {
if (remoteSemanticVersion.length < i + 1) {
return false;
}
if (localSemanticVersion[i] < remoteSemanticVersion[i]) {
return true;
} else if (localSemanticVersion[i] > remoteSemanticVersion[i]) {
return false;
}
}
return false;
} catch (NumberFormatException e) {
this.plugin.getLogger().warning("Invalid version number found: " + localVersion + " or " + remoteVersion);
return true;
}
}
/**
* Parse the version number from a string. This expects the version number to be consisting of numbers separated by dots.
*
* @param version the version string to parse
* @return the parsed version number
* @throws NumberFormatException if the version number is not in the expected format
*/
private int[] parseVersion(String version) {
final String[] split = version.split("\\.");
final int[] semanticVersion = new int[split.length];
for (int i = 0; i < split.length; i++) {
semanticVersion[i] = Integer.parseInt(split[i]);
}
return semanticVersion;
} }
/** /**
@ -539,7 +606,7 @@ public class Updater {
conn.setConnectTimeout(5000); conn.setConnectTimeout(5000);
if (this.apiKey != null) { if (this.apiKey != null) {
conn.addRequestProperty("X-API-Key", this.apiKey); conn.addRequestProperty("Authorization", this.apiKey);
} }
conn.addRequestProperty("User-Agent", Updater.USER_AGENT); conn.addRequestProperty("User-Agent", Updater.USER_AGENT);
@ -557,18 +624,26 @@ public class Updater {
} }
this.versionName = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.TITLE_VALUE); this.versionName = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.TITLE_VALUE);
this.versionLink = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.LINK_VALUE); JSONArray versionFiles = (JSONArray) ((JSONObject) array.get(array.size() - 1)).get(Updater.FILES_VALUE);
for (Object versionFile : versionFiles) {
JSONObject file = (JSONObject) versionFile;
if (file.get("primary").equals(true)) {
versionLink = (String) file.get(Updater.LINK_VALUE);
versionHash = ((JSONObject) file.get("hashes")).get("sha512").toString();
break;
}
}
this.versionType = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.TYPE_VALUE); this.versionType = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.TYPE_VALUE);
this.versionGameVersion = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.VERSION_VALUE); JSONArray gameVersions = (JSONArray) ((JSONObject) array.get(array.size() - 1)).get(Updater.VERSION_VALUE);
this.versionGameVersion = gameVersions.get(gameVersions.size() - 1).toString();
return true; return true;
} catch (final IOException e) { } catch (final IOException e) {
if (e.getMessage().contains("HTTP response code: 403")) { if (e.getMessage().contains("HTTP response code: 403")) {
this.plugin.getLogger().severe("dev.bukkit.org rejected the API key provided in plugins/Updater/config.yml"); this.plugin.getLogger().severe("The Modrinth API server rejected the API key provided in plugins/Updater/config.yml");
this.plugin.getLogger().severe("Please double-check your configuration to ensure it is correct."); this.plugin.getLogger().severe("Please double-check your configuration to ensure it is correct.");
this.result = UpdateResult.FAIL_APIKEY; this.result = UpdateResult.FAIL_APIKEY;
} else { } else {
this.plugin.getLogger().severe("The updater could not contact dev.bukkit.org for updating."); this.plugin.getLogger().severe("The updater could not contact the api.modrinth.com server for updating.");
this.plugin.getLogger().severe("If you have not recently modified your configuration and this is the first time you are seeing this message, the site may be experiencing temporary downtime."); this.plugin.getLogger().severe("If you have not recently modified your configuration and this is the first time you are seeing this message, the site may be experiencing temporary downtime.");
this.result = UpdateResult.FAIL_DBO; this.result = UpdateResult.FAIL_DBO;
} }
@ -592,7 +667,7 @@ public class Updater {
final String[] split = Updater.this.versionLink.split("/"); final String[] split = Updater.this.versionLink.split("/");
name = split[split.length - 1]; name = split[split.length - 1];
} }
Updater.this.saveFile(new File(Updater.this.plugin.getDataFolder().getParent(), Updater.this.updateFolder), name, Updater.this.versionLink); Updater.this.saveFile(Updater.this.plugin.getServer().getUpdateFolderFile(), name, Updater.this.versionLink);
} else { } else {
Updater.this.result = UpdateResult.UPDATE_AVAILABLE; Updater.this.result = UpdateResult.UPDATE_AVAILABLE;
} }

View File

@ -53,7 +53,7 @@ public class ItemUtil {
* @return ItemStack's name * @return ItemStack's name
*/ */
public static String getName(ItemStack itemStack, int maxWidth) { public static String getName(ItemStack itemStack, int maxWidth) {
String code = ChestShop.callEvent(new ItemStringQueryEvent(itemStack)).getItemString(); String code = ChestShop.callEvent(new ItemStringQueryEvent(itemStack, maxWidth)).getItemString();
if (code != null) { if (code != null) {
if (maxWidth > 0) { if (maxWidth > 0) {
int codeWidth = getMinecraftStringWidth(code); int codeWidth = getMinecraftStringWidth(code);

View File

@ -221,7 +221,7 @@ public class uBlock {
Sign sign = (Sign) faceBlock.getState(); Sign sign = (Sign) faceBlock.getState();
Container signContainer = findConnectedContainer(sign); Container signContainer = findConnectedContainer(sign);
if (!chestBlock.equals(signContainer.getBlock())) { if (signContainer == null || !chestBlock.equals(signContainer.getBlock())) {
continue; continue;
} }
@ -317,6 +317,7 @@ public class uBlock {
} }
public static boolean couldBeShopContainer(InventoryHolder holder) { public static boolean couldBeShopContainer(InventoryHolder holder) {
return holder instanceof Container && couldBeShopContainer(((Container) holder).getBlock()); return (holder instanceof Container && couldBeShopContainer(((Container) holder).getBlock()))
|| (holder instanceof DoubleChest && couldBeShopContainer(((DoubleChest) holder).getLeftSide()));
} }
} }

View File

@ -18,6 +18,24 @@ iteminfo_book: |-
&fBook Pages: &7%pages &fBook Pages: &7%pages
iteminfo_book_generation: "&fBook Generation: &7%generation" iteminfo_book_generation: "&fBook Generation: &7%generation"
iteminfo_leather_color: "&fLeather Color: &7Red: %colorred, Green: %colorgreen, Blue: %colorblue (Hex: #%colorhex)"
iteminfo_bundle_items: "&fBundle Items: &7%itemcount"
iteminfo_axolotl_variant: "&fAxolotl Variant: &7%variant"
iteminfo_recipes: "&fRecipe Knowledge:"
iteminfo_map_view: |-
&fMap ID: &7%id
&fMap World: &7%world
&fMap Center: &7%x/%z
&fMap Locked: &7locked
iteminfo_map_location: "&fMap Location: &7%location"
iteminfo_tropical_fish: |-
&fFish Pattern: &7%pattern
&fFish Pattern Color: &7%patterncolor
&fFish Body Color: &7%bodycolor
iteminfo_crossbow_projectiles: "&fCharged Crossbow Projectile:"
iteminfo_crossbow_projectile: "&f%item"
iteminfo_lore: |- iteminfo_lore: |-
&fLore: &fLore:
&r%lore &r%lore
@ -63,6 +81,7 @@ INVALID_SHOP_DETECTED: "The shop cannot be used!"
INVALID_SHOP_PRICE: "The shop has an invalid price!" INVALID_SHOP_PRICE: "The shop has an invalid price!"
INVALID_SHOP_QUANTITY: "The shop has an invalid quantity!" INVALID_SHOP_QUANTITY: "The shop has an invalid quantity!"
CANNOT_ACCESS_THE_CHEST: "You don't have permissions to access this chest!" CANNOT_ACCESS_THE_CHEST: "You don't have permissions to access this chest!"
CANNOT_CHANGE_SIGN_BACKSIDE: "You can't change the back side of a shop sign!"
SELL_PRICE_HIGHER_THAN_BUY_PRICE: "Sell price is above the buy price!" SELL_PRICE_HIGHER_THAN_BUY_PRICE: "Sell price is above the buy price!"
SELL_PRICE_ABOVE_MAX: "Sell price %price is above maximum %maxprice!" SELL_PRICE_ABOVE_MAX: "Sell price %price is above maximum %maxprice!"

View File

@ -48,7 +48,7 @@ INVALID_SHOP_DETECTED: "La tienda no puede ser usada."
INVALID_SHOP_PRICE: "¡El precio de la tienda no es valido!" INVALID_SHOP_PRICE: "¡El precio de la tienda no es valido!"
INVALID_SHOP_QUANTITY: "¡Cantidad no valida en la tienda!" INVALID_SHOP_QUANTITY: "¡Cantidad no valida en la tienda!"
CANNOT_ACCESS_THE_CHEST: "Tu no tienes permiso para acceder a este cofre." CANNOT_ACCESS_THE_CHEST: "Tu no tienes permiso para acceder a este cofre."
SELL_PRICE_HIGHER_THAN_BUY_PRICE: "Sell price is above the buy price!" SELL_PRICE_HIGHER_THAN_BUY_PRICE: "¡El precio de venta está por encima del precio de compra!"
SELL_PRICE_ABOVE_MAX: "¡El precio de venta %price está por encima del máximo %maxprice!" SELL_PRICE_ABOVE_MAX: "¡El precio de venta %price está por encima del máximo %maxprice!"
SELL_PRICE_BELOW_MIN: "¡El precio de venta %price está por debajo del mínimo %minprice!" SELL_PRICE_BELOW_MIN: "¡El precio de venta %price está por debajo del mínimo %minprice!"
BUY_PRICE_ABOVE_MAX: "¡El precio de compra %price está por encima del máximo %maxprice!" BUY_PRICE_ABOVE_MAX: "¡El precio de compra %price está por encima del máximo %maxprice!"

View File

@ -41,9 +41,9 @@ NOT_ENOUGH_ITEMS_TO_SELL: "Vous n'avez pas assez d'objet à vendre"
NOT_ENOUGH_SPACE_IN_YOUR_SHOP: "[La boutique%price %item&7 est pleine!](&f%seller&7 a essayé de vendre\n&7Dans le monde : &f%world\n&7À la position: &f%x/%y/%z)" NOT_ENOUGH_SPACE_IN_YOUR_SHOP: "[La boutique%price %item&7 est pleine!](&f%seller&7 a essayé de vendre\n&7Dans le monde : &f%world\n&7À la position: &f%x/%y/%z)"
NOT_ENOUGH_STOCK: "Ce Magasin est vide." NOT_ENOUGH_STOCK: "Ce Magasin est vide."
NOT_ENOUGH_STOCK_IN_YOUR_SHOP: "[La boutique%price %item&7 est en rupture!](&f%buyer&7 a essayé d'acheter\n&7Dans le monde : &f%world\n&7À la position: &f%x/%y/%z)" NOT_ENOUGH_STOCK_IN_YOUR_SHOP: "[La boutique%price %item&7 est en rupture!](&f%buyer&7 a essayé d'acheter\n&7Dans le monde : &f%world\n&7À la position: &f%x/%y/%z)"
YOU_BOUGHT_FROM_SHOP: "Vous avez acheté %amount %item a %owner pour %price." YOU_BOUGHT_FROM_SHOP: "Vous avez acheté %item a %owner pour %price."
SOMEBODY_BOUGHT_FROM_YOUR_SHOP: "[%buyer a acheté %item pour %price.](&7Dans le monde: &f%world\n&7Aux coordonnées: &f%x/%y/%z)" SOMEBODY_BOUGHT_FROM_YOUR_SHOP: "[%buyer a acheté %item pour %price.](&7Dans le monde: &f%world\n&7Aux coordonnées: &f%x/%y/%z)"
YOU_SOLD_TO_SHOP: "Vous avez vendu %amount %item à %buyer pour %price." YOU_SOLD_TO_SHOP: "Vous avez vendu %item à %buyer pour %price."
SOMEBODY_SOLD_TO_YOUR_SHOP: "[%seller a vendu %item pour %price.](&7Dans le monde: &f%world\n&7Aux coordonnées: &f%x/%y/%z)" SOMEBODY_SOLD_TO_YOUR_SHOP: "[%seller a vendu %item pour %price.](&7Dans le monde: &f%world\n&7Aux coordonnées: &f%x/%y/%z)"
YOU_CANNOT_CREATE_SHOP: "Vous ne pouvez pas créer ce type de Magasin !" YOU_CANNOT_CREATE_SHOP: "Vous ne pouvez pas créer ce type de Magasin !"
NO_CHEST_DETECTED: "Impossible de trouver le coffre !" NO_CHEST_DETECTED: "Impossible de trouver le coffre !"

View File

@ -24,13 +24,13 @@ METRICS: |-
&fAccount: &7%accounts &fAccount: &7%accounts
&fTransazioni medie: &7%totalTransactions &f(acquisti: &7%buyTransactions &fvendite: &7%sellTransactions&f) &fTransazioni medie: &7%totalTransactions &f(acquisti: &7%buyTransactions &fvendite: &7%sellTransactions&f)
&fMedia oggetti scambiati: &7%totalItems &f(acquisti: &7%boughtItems &fvendite: &7%soldItems&f) &fMedia oggetti scambiati: &7%totalItems &f(acquisti: &7%boughtItems &fvendite: &7%soldItems&f)
ACCESS_DENIED: "Non hai il permesso per farlo!" ACCESS_DENIED: "Non hai i permessi per accedere al contenitore di quello shop!"
TRADE_DENIED: "Non hai il permesso per usare quello shop!" TRADE_DENIED: "Non hai il permesso per usare quello shop!"
TRADE_DENIED_ACCESS_PERMS: "Non puoi usare shop a cui hai accesso!" TRADE_DENIED_ACCESS_PERMS: "Non puoi usare shop a cui hai accesso!"
TRADE_DENIED_CREATIVE_MODE: "Non puoi usare shop mentre sei in modalità creativa!" TRADE_DENIED_CREATIVE_MODE: "Non puoi usare shop mentre sei in modalità creativa!"
NOT_ENOUGH_MONEY: "Non hai abbastanza soldi!" NOT_ENOUGH_MONEY: "Non hai abbastanza soldi!"
NOT_ENOUGH_MONEY_SHOP: "Il proprietario del negozio non ha abbastanza soldi!" NOT_ENOUGH_MONEY_SHOP: "Il proprietario del negozio non ha abbastanza soldi!"
CLIENT_DEPOSIT_FAILED: "Accredito di denaro fallito" CLIENT_DEPOSIT_FAILED: "Deposito di denaro fallito!"
SHOP_DEPOSIT_FAILED: "Deposito di denaro al proprietario del negozio non riuscita!" SHOP_DEPOSIT_FAILED: "Deposito di denaro al proprietario del negozio non riuscita!"
NO_ECONOMY_ACCOUNT: "L'account Economy del proprietario dello shop non esiste!" NO_ECONOMY_ACCOUNT: "L'account Economy del proprietario dello shop non esiste!"
NO_BUYING_HERE: "Non puoi comprare qui!" NO_BUYING_HERE: "Non puoi comprare qui!"
@ -41,12 +41,12 @@ NOT_ENOUGH_ITEMS_TO_SELL: "Non hai abbastanza oggetti da vendere!"
NOT_ENOUGH_SPACE_IN_YOUR_SHOP: "[%price %item&7 è pieno!](&f%seller&7 ha provato a vendere\n&7Nel mondo: &f%world\n&7In posizione: &f%x/%y/%z)" NOT_ENOUGH_SPACE_IN_YOUR_SHOP: "[%price %item&7 è pieno!](&f%seller&7 ha provato a vendere\n&7Nel mondo: &f%world\n&7In posizione: &f%x/%y/%z)"
NOT_ENOUGH_STOCK: "Gli oggetti di questo negozio sono esauriti." NOT_ENOUGH_STOCK: "Gli oggetti di questo negozio sono esauriti."
NOT_ENOUGH_STOCK_IN_YOUR_SHOP: "[%price %item&7 ha esaurito oggetti!](&f%buyer&7 ha provato a comprare\n&7Nel mondo: &f%world\n&7In posizione: &f%x/%y/%z)" NOT_ENOUGH_STOCK_IN_YOUR_SHOP: "[%price %item&7 ha esaurito oggetti!](&f%buyer&7 ha provato a comprare\n&7Nel mondo: &f%world\n&7In posizione: &f%x/%y/%z)"
YOU_BOUGHT_FROM_SHOP: "Hai comprato %amount %item da %owner per %price." YOU_BOUGHT_FROM_SHOP: "Hai comprato %item da %owner per %price."
SOMEBODY_BOUGHT_FROM_YOUR_SHOP: "[%buyer ha comprato %item per %price.](&7Nel mondo: &f%world\n&7In posizione: &f%x/%y/%z)" SOMEBODY_BOUGHT_FROM_YOUR_SHOP: "[%buyer ha comprato %item per %price.](&7Nel mondo: &f%world\n&7In posizione: &f%x/%y/%z)"
YOU_SOLD_TO_SHOP: "Hai venduto %amount %item a %buyer per %price." YOU_SOLD_TO_SHOP: "Hai venduto %item a %buyer per %price."
SOMEBODY_SOLD_TO_YOUR_SHOP: "[%seller ha venduto %item per %price.](&7Nel mondo: &f%world\n&7In posizione: &f%x/%y/%z)" SOMEBODY_SOLD_TO_YOUR_SHOP: "[%seller ha venduto %item per %price.](&7Nel mondo: &f%world\n&7In posizione: &f%x/%y/%z)"
YOU_CANNOT_CREATE_SHOP: "Non puoi creare negozi di questo tipo!" YOU_CANNOT_CREATE_SHOP: "Non puoi creare negozi di questo tipo!"
NO_CHEST_DETECTED: "Non e stata trovata alcuna chest!" NO_CHEST_DETECTED: "Non è stata trovata alcuna chest!"
INVALID_SHOP_DETECTED: "Il negozio non può essere utilizzato!" INVALID_SHOP_DETECTED: "Il negozio non può essere utilizzato!"
INVALID_SHOP_PRICE: "Il prezzo inserito non è valido!" INVALID_SHOP_PRICE: "Il prezzo inserito non è valido!"
INVALID_SHOP_QUANTITY: "La quantità inserita non è valida!" INVALID_SHOP_QUANTITY: "La quantità inserita non è valida!"
@ -58,11 +58,11 @@ BUY_PRICE_ABOVE_MAX: "Il prezzo di acquisto %price è superiore al massimo %maxp
BUY_PRICE_BELOW_MIN: "Il prezzo di acquisto %price è inferiore al minimo %minprice!" BUY_PRICE_BELOW_MIN: "Il prezzo di acquisto %price è inferiore al minimo %minprice!"
CLICK_TO_AUTOFILL_ITEM: "Clicca il cartello con l'oggetto che vuoi commerciare nello shop!" CLICK_TO_AUTOFILL_ITEM: "Clicca il cartello con l'oggetto che vuoi commerciare nello shop!"
NO_ITEM_IN_HAND: "Non hai un oggetto in mano per riempire automaticamente!" NO_ITEM_IN_HAND: "Non hai un oggetto in mano per riempire automaticamente!"
PROTECTED_SHOP: "Negozio protetto con successo!" PROTECTED_SHOP: "Negozio protetto con successo tramite LWC!"
PROTECTED_SHOP_SIGN: "Cartello dello shop protetto con successo tramite LWC!" PROTECTED_SHOP_SIGN: "Cartello dello shop protetto con successo tramite LWC!"
SHOP_CREATED: "Negozio creato con successo!" SHOP_CREATED: "Negozio creato con successo!"
SHOP_FEE_PAID: "È è stato addebitato %amount" SHOP_FEE_PAID: "Ti è stato addebitato %amount"
SHOP_REFUNDED: "Sei stato rimborsato %amount." SHOP_REFUNDED: "Sei stato rimborsato di %amount."
ITEM_GIVEN: "Dato %item a %player." ITEM_GIVEN: "Dato %item a %player."
RESTRICTED_SIGN_CREATED: "Cartello creato con successo!" RESTRICTED_SIGN_CREATED: "Cartello creato con successo!"
PLAYER_NOT_FOUND: "Giocatore non trovato!" PLAYER_NOT_FOUND: "Giocatore non trovato!"

View File

@ -50,7 +50,7 @@ INVALID_SHOP_DETECTED: "Магазин не работает!"
INVALID_SHOP_PRICE: "Указана недопустимая цена!" INVALID_SHOP_PRICE: "Указана недопустимая цена!"
INVALID_SHOP_QUANTITY: "Указано недопустимое количество!" INVALID_SHOP_QUANTITY: "Указано недопустимое количество!"
CANNOT_ACCESS_THE_CHEST: "У вас нет разрешения для доступа к этому магазину!" CANNOT_ACCESS_THE_CHEST: "У вас нет разрешения для доступа к этому магазину!"
SELL_PRICE_HIGHER_THAN_BUY_PRICE: "Sell price is above the buy price!" SELL_PRICE_HIGHER_THAN_BUY_PRICE: "Цена продажи выше цены покупки!"
SELL_PRICE_ABOVE_MAX: "Цена продажи: %price превышает максимальную: %maxprice!" SELL_PRICE_ABOVE_MAX: "Цена продажи: %price превышает максимальную: %maxprice!"
SELL_PRICE_BELOW_MIN: "Цена продажи: %price ниже минимальной: %minprice!" SELL_PRICE_BELOW_MIN: "Цена продажи: %price ниже минимальной: %minprice!"
BUY_PRICE_ABOVE_MAX: "Цена покупки: %price превышает максимальную: %maxprice!" BUY_PRICE_ABOVE_MAX: "Цена покупки: %price превышает максимальную: %maxprice!"

View File

@ -2,25 +2,25 @@
prefix: "&a[Shop] &r" prefix: "&a[Shop] &r"
shopinfo: |- shopinfo: |-
&aDükkan Bilgisi: &aDükkan Bilgisi:
&fSahip:&7 %owner &fSahip: &7%owner
&fStok: &7%stock &fStok: &7%stock
&fEşya:&7 %item &fEşya: &7%item
shopinfo_buy: "&7%price &fücret karşılığında &7%amount &fadet satın al" shopinfo_buy: "&7%price &fücret karşılığında &7%amount &fadet satın al"
shopinfo_sell: "&7%price &fücret karşılığında &7%amount &fadet sat" shopinfo_sell: "&7%price &fücret karşılığında &7%amount &fadet sat"
iteminfo: "&aEşya Bilgisi: &r" iteminfo: "&aEşya Bilgisi: &r"
iteminfo_fullname: "&fTam İsim: &7%item" iteminfo_fullname: "&fTam İsim: &7%item"
iteminfo_shopname: "&fDükkan Tabelası: &7%item" iteminfo_shopname: "&fDükkan Tabelası: &7%item"
iteminfo_repaircost: "&fTamir Ücreti:&7 %cost" iteminfo_repaircost: "&fTamir Ücreti: &7%cost"
iteminfo_book: |- iteminfo_book: |-
&fKitap Başlığı:&7 %title &fKitap Başlığı: &7%title
&fKitap Yazarı:&7 %author &fKitap Yazarı: &7%author
&fKitap Sayfaları:&7 %pages &fKitap Sayfaları: &7%pages
iteminfo_book_generation: "&fKitap Jenerasyonu:&7 %generation" iteminfo_book_generation: "&fKitap Jenerasyonu: &7%generation"
iteminfo_lore: |- iteminfo_lore: |-
&fAçıklama: &fAçıklama:
&r%lore &r%lore
METRICS: |- METRICS: |-
&a[Market] &fÖlçütler: &a[Shop] &fÖlçütler:
&fHesaplar:&7 %accounts &fHesaplar:&7 %accounts
&fİşlem ortalamaları:&7 %totalTransactions &f(alış:&7 %buyTransactions &fsatış:&7 %sellTransactions&f) &fİşlem ortalamaları:&7 %totalTransactions &f(alış:&7 %buyTransactions &fsatış:&7 %sellTransactions&f)
&fEşya ticareti ortalamaları:&7 %totalItems &f(alınan:&7 %boughtItems &fsatılan:&7 %soldItems&f) &fEşya ticareti ortalamaları:&7 %totalItems &f(alınan:&7 %boughtItems &fsatılan:&7 %soldItems&f)

View File

@ -1,78 +1,78 @@
--- ---
prefix: "&a[Shop] &r" prefix: "&a[Магазин] &r"
shopinfo: |- shopinfo: |-
&aShop Information: &aІнформація про магазин:
&fOwner: &7%owner &fВласник: &7%owner
&fStock: &7%stock &fЗалишок: &7%stock
&fItem: &7%item &fПредмет: &7%item
shopinfo_buy: "&fBuy &7%amount &ffor &7%price" shopinfo_buy: "&fПридбати &7%amount &fза &7%price"
shopinfo_sell: "&fSell &7%amount &ffor &7%price" shopinfo_sell: "&fПродати &7%amount &fза &7%price"
iteminfo: "&aItem Information: &r" iteminfo: "&aІнформація про предмет: &r"
iteminfo_fullname: "&fFull Name: &7%item" iteminfo_fullname: "&fПовна назва: &7%item"
iteminfo_shopname: "&fShop Sign: &7%item" iteminfo_shopname: "&fНазва магазину: &7%item"
iteminfo_repaircost: "&fRepair Cost: &7%cost" iteminfo_repaircost: "&fВартість ремонту: &7%cost"
iteminfo_book: |- iteminfo_book: |-
&fBook Title: &7%title &fЗаголовок книги: &7%title
&fBook Author: &7%author &fАвтор книги: &7%author
&fBook Pages: &7%pages &fСторінок книги: &7%pages
iteminfo_book_generation: "&fBook Generation: &7%generation" iteminfo_book_generation: "&fВерсія книги: &7%generation"
iteminfo_lore: |- iteminfo_lore: |-
&fLore: &fОпис:
&r%lore &r%lore
METRICS: |- METRICS: |-
&a[Shop] &fMetrics: &a[Магазин] &fСтатистика:
&fAccounts: &7%accounts &fГравців: &7%accounts
&fAverage transactions: &7%totalTransactions &f(buy: &7%buyTransactions &fsell: &7%sellTransactions&f) &fСередня кількість трансакцій: &7%totalTransactions &f(Купівля: &7%buyTransactions &fПродаж: &7%sellTransactions&f)
&fAverage items traded: &7%totalItems &f(bought: &7%boughtItems &fsold: &7%soldItems&f) &fСередня кількість проданих предметів: &7%totalItems &f(Придбано: &7%boughtItems &fПродано: &7%soldItems&f)
ACCESS_DENIED: "You don't have permission to access that shop's storage container!" ACCESS_DENIED: "Ви не маєте дозволу на те, щоб відкривати сховище цього магазину!"
TRADE_DENIED: "You don't have permission to trade with that shop!" TRADE_DENIED: "Ви не маєте дозволу на торгівлю в цьому магазині!"
TRADE_DENIED_ACCESS_PERMS: "You cannot trade with shops that you have access to!" TRADE_DENIED_ACCESS_PERMS: "Ви не можете торгувати в магазинах, до яких у вас є доступ!"
TRADE_DENIED_CREATIVE_MODE: "You cannot trade with shops while you are in creative mode!" TRADE_DENIED_CREATIVE_MODE: "Ви не можете торгувати доки ви в режимі творчости!"
NOT_ENOUGH_MONEY: "You don't have enough money!" NOT_ENOUGH_MONEY: "Вам не вистачає грошей!"
NOT_ENOUGH_MONEY_SHOP: "Shop owner doesn't have enough money!" NOT_ENOUGH_MONEY_SHOP: "Власник магазину не має достатньо грошей!"
CLIENT_DEPOSIT_FAILED: "Money deposit to your account failed!" CLIENT_DEPOSIT_FAILED: "Не вдалось покласти гроші на ваш рахунок!"
SHOP_DEPOSIT_FAILED: "Money deposit to shop owner failed!" SHOP_DEPOSIT_FAILED: "Не вдалось покласти гроші на рахунок власника магазину!"
NO_ECONOMY_ACCOUNT: "Economy account from shop owner doesn't exist!" NO_ECONOMY_ACCOUNT: "Рахунок власника магазину не існує!"
NO_BUYING_HERE: "You can't buy here!" NO_BUYING_HERE: "Ви не можете тут нічого придбати!"
NO_SELLING_HERE: "You can't sell here!" NO_SELLING_HERE: "Ви не можете тут нічого продавати!"
NOT_ENOUGH_SPACE_IN_INVENTORY: "You haven't got enough space in inventory!" NOT_ENOUGH_SPACE_IN_INVENTORY: "У вас недостатньо місця в інвентарі!"
NOT_ENOUGH_SPACE_IN_CHEST: "There isn't enough space in chest!" NOT_ENOUGH_SPACE_IN_CHEST: "Недостатньо місця в скрині!"
NOT_ENOUGH_ITEMS_TO_SELL: "You don't have enough items to sell!" NOT_ENOUGH_ITEMS_TO_SELL: "У вас недостатньо речей, для продажу!"
NOT_ENOUGH_SPACE_IN_YOUR_SHOP: "[%price %item&7 shop is full!](&f%seller&7 tried to sell\n&7In world: &f%world\n&7At position: &f%x/%y/%z)" NOT_ENOUGH_SPACE_IN_YOUR_SHOP: "[Магазин з %item&7 за %price заповнений!](&f%seller&7 намагався здійснити продаж.\n&7У світі: &f%world\n&7На координатах: &f%x/%y/%z)"
NOT_ENOUGH_STOCK: "This shop is out of stock." NOT_ENOUGH_STOCK: "Товар в магазині закінчився."
NOT_ENOUGH_STOCK_IN_YOUR_SHOP: "[%price %item&7 shop is out of stock!](&f%buyer&7 tried to buy\n&7In world: &f%world\n&7At position: &f%x/%y/%z)" NOT_ENOUGH_STOCK_IN_YOUR_SHOP: "[Магазин з %item&7 за %price більше не має товару!](&f%buyer&7 намагався здійснити купівлю.\n&7У світі: &f%world\n&7На координатах: &f%x/%y/%z)"
YOU_BOUGHT_FROM_SHOP: "You bought %item from %owner for %price." YOU_BOUGHT_FROM_SHOP: "Ви придбали %item від %owner за %price."
SOMEBODY_BOUGHT_FROM_YOUR_SHOP: "[%buyer bought %item for %price.](&7In world: &f%world\n&7At position: &f%x/%y/%z)" SOMEBODY_BOUGHT_FROM_YOUR_SHOP: "[%buyer придбав %item за %price](&7У світі: &f%world\n&7На координатах: &f%x/%y/%z)"
YOU_SOLD_TO_SHOP: "You sold %item to %buyer for %price." YOU_SOLD_TO_SHOP: "Ви продали %item гравцю %buyer за %price."
SOMEBODY_SOLD_TO_YOUR_SHOP: "[%seller sold %item for %price.](&7In world: &f%world\n&7At position: &f%x/%y/%z)" SOMEBODY_SOLD_TO_YOUR_SHOP: "[%seller продав %item за %price](&7У світі: &f%world\n&7На координатах: &f%x/%y/%z)"
YOU_CANNOT_CREATE_SHOP: "You can't create this type of shop!" YOU_CANNOT_CREATE_SHOP: "Ви не можете створити магазин цього типу!"
NO_CHEST_DETECTED: "Couldn't find a chest!" NO_CHEST_DETECTED: "Скриню не знайдено!"
INVALID_SHOP_DETECTED: "The shop cannot be used!" INVALID_SHOP_DETECTED: "Цей магазин не можна використовувати!"
INVALID_SHOP_PRICE: "The shop has an invalid price!" INVALID_SHOP_PRICE: "Цей магазин має невірну ціну!"
INVALID_SHOP_QUANTITY: "The shop has an invalid quantity!" INVALID_SHOP_QUANTITY: "Цей магазин має невірну кількість!"
CANNOT_ACCESS_THE_CHEST: "You don't have permissions to access this chest!" CANNOT_ACCESS_THE_CHEST: "Ви не маєте доступу до цієї скрині!"
SELL_PRICE_HIGHER_THAN_BUY_PRICE: "Sell price is above the buy price!" SELL_PRICE_HIGHER_THAN_BUY_PRICE: "Ціна продажу більша за ціну купівлі!"
SELL_PRICE_ABOVE_MAX: "Sell price %price is above maximum %maxprice!" SELL_PRICE_ABOVE_MAX: "Ціна продажу %price більша за максимальну %maxprice!"
SELL_PRICE_BELOW_MIN: "Sell price %price is below minimum %minprice!" SELL_PRICE_BELOW_MIN: "Ціна продажу %price менша за мінімальну %minprice!"
BUY_PRICE_ABOVE_MAX: "Buy price %price is above maximum %maxprice!" BUY_PRICE_ABOVE_MAX: "Ціна купівлі %price більша за максимальну %maxprice!"
BUY_PRICE_BELOW_MIN: "Buy price %price is below minimum %minprice!" BUY_PRICE_BELOW_MIN: "Ціна купівлі %price менша за мінімальну %minprice!"
CLICK_TO_AUTOFILL_ITEM: "Click the sign with the item that this shop is for!" CLICK_TO_AUTOFILL_ITEM: "Натисніть на табличку з предметом для якої призначений цей магазин!"
NO_ITEM_IN_HAND: "You don't have an item in your hand to autofill!" NO_ITEM_IN_HAND: "Ви не маєте предмета в вашій руці для автозаповнення!"
PROTECTED_SHOP: "Successfully protected the shop with LWC!" PROTECTED_SHOP: "Магазин успішно захищений з LWC!"
PROTECTED_SHOP_SIGN: "Successfully protected the shop sign with LWC!" PROTECTED_SHOP_SIGN: "Успішно захищено табличку магазину з LWC!"
SHOP_CREATED: "Shop successfully created!" SHOP_CREATED: "Магазин успішно створений!"
SHOP_FEE_PAID: "You have been charged %amount" SHOP_FEE_PAID: "З вас стягнуто %amount"
SHOP_REFUNDED: "You have been refunded %amount." SHOP_REFUNDED: "Вам повернено %amount."
ITEM_GIVEN: "Given %item to %player." ITEM_GIVEN: "Видано %item гравцеві %player."
RESTRICTED_SIGN_CREATED: "Sign successfully created!" RESTRICTED_SIGN_CREATED: "Табличка успішно створена!"
PLAYER_NOT_FOUND: "Player not found!" PLAYER_NOT_FOUND: "Гравця не знайдено!"
NO_PERMISSION: "You don't have permissions to do that!" NO_PERMISSION: "У вас немає дозволу для цього!"
INCORRECT_ITEM_ID: "You have specified an invalid item id!" INCORRECT_ITEM_ID: "Ви вказали неправильний ID предмета!"
INVALID_CLIENT_NAME: "Your username is not a valid Minecraft username!" INVALID_CLIENT_NAME: "Ваш нікнейм не є допустимим Minecraft нікнеймом!"
NOT_ENOUGH_PROTECTIONS: "Could not create a protection!" NOT_ENOUGH_PROTECTIONS: "Не вдалося створити захист!"
NO_SHOP_FOUND: "No shop found." NO_SHOP_FOUND: "Магазин не знайдено."
CANNOT_CREATE_SHOP_HERE: "You can't create shop here!" CANNOT_CREATE_SHOP_HERE: "Ви не можете створити магазин тут!"
TOGGLE_MESSAGES_OFF: "You will no longer receive messages from your shop(s)." TOGGLE_MESSAGES_OFF: "Ви тепер не отримуватимете повідомлення від ваших магазинів."
TOGGLE_MESSAGES_ON: "You will now receive messages from your shop(s)." TOGGLE_MESSAGES_ON: "Ви тепер отримуватимете повідомлення від ваших магазинів."
TOGGLE_ACCESS_ON: "You can no longer trade at shops that you have access to" TOGGLE_ACCESS_ON: "Ви більше не можете торгувати в магазинах до яких у вас є доступ"
TOGGLE_ACCESS_OFF: "You can now trade at shops that you have access to" TOGGLE_ACCESS_OFF: "Ви тепер можете торгувати з магазинами до яких у вас є доступ"

View File

@ -51,7 +51,7 @@ INVALID_SHOP_DETECTED: "这个商店无法使用!"
INVALID_SHOP_PRICE: "该商店的价格无效!" INVALID_SHOP_PRICE: "该商店的价格无效!"
INVALID_SHOP_QUANTITY: "商店有无效的数量!" INVALID_SHOP_QUANTITY: "商店有无效的数量!"
CANNOT_ACCESS_THE_CHEST: "你没有权限打开这个箱子!" CANNOT_ACCESS_THE_CHEST: "你没有权限打开这个箱子!"
SELL_PRICE_HIGHER_THAN_BUY_PRICE: "Sell price is above the buy price!" SELL_PRICE_HIGHER_THAN_BUY_PRICE: "售价高于购买价格!"
SELL_PRICE_ABOVE_MAX: "Sell price %price is above maximum %maxprice!" SELL_PRICE_ABOVE_MAX: "Sell price %price is above maximum %maxprice!"
SELL_PRICE_BELOW_MIN: "Sell price %price is below minimum %minprice!" SELL_PRICE_BELOW_MIN: "Sell price %price is below minimum %minprice!"
BUY_PRICE_ABOVE_MAX: "Buy price %price is above maximum %maxprice!" BUY_PRICE_ABOVE_MAX: "Buy price %price is above maximum %maxprice!"

View File

@ -4,8 +4,9 @@ version: '${bukkit.plugin.version}'
author: Acrobot author: Acrobot
authors: ['https://github.com/ChestShop-authors/ChestShop-3/contributors'] authors: ['https://github.com/ChestShop-authors/ChestShop-3/contributors']
description: A chest shop for economy plugins. description: A chest shop for economy plugins.
softdepend: [Vault, Reserve, LWC, Lockette, LockettePro, Deadbolt, BlockLocker, OddItem, WorldGuard, GriefPrevention, RedProtect, Heroes, SimpleChestLock, Residence, ShowItem] softdepend: [Vault, Reserve, LWC, Lockette, LockettePro, Deadbolt, BlockLocker, OddItem, WorldGuard, GriefPrevention, RedProtect, Heroes, SimpleChestLock, Residence, ShowItem, ItemBridge]
api-version: '1.13' api-version: '1.13'
folia-supported: true
commands: commands:
iteminfo: iteminfo:
@ -122,6 +123,22 @@ permissions:
description: Gives you the power to do access shops for all names. description: Gives you the power to do access shops for all names.
ChestShop.othername.access.(some name): ChestShop.othername.access.(some name):
description: Gives you the power to do access shops for (some name), for example your town. description: Gives you the power to do access shops for (some name), for example your town.
ChestShop.nolimit.buy.min:
description: Allows user to bypass the minimal buy price for all items
ChestShop.nolimit.buy.min.(itemType):
description: Allows user to bypass the minimal buy price for itemType
ChestShop.nolimit.buy.max:
description: Allows user to bypass the maximal buy price for all items
ChestShop.nolimit.buy.max.(itemType):
description: Allows user to bypass the maximal buy price for itemType
ChestShop.nolimit.sell.min:
description: Allows user to bypass the minimal sell price for all items
ChestShop.nolimit.sell.min.(itemType):
description: Allows user to bypass the minimal sell price for itemType
ChestShop.nolimit.sell.max:
description: Allows user to bypass the maximal sell price for all items
ChestShop.nolimit.sell.max.(itemType):
description: Allows user to bypass the maximal sell price for itemType
ChestShop.shop.create.food: ChestShop.shop.create.food:
description: Allows to create a shop that sells food description: Allows to create a shop that sells food
children: children:

View File

@ -1,12 +1,14 @@
package com.Acrobot.Breeze.Tests; package com.Acrobot.Breeze.Tests;
import com.Acrobot.Breeze.Utils.MaterialUtil; import com.Acrobot.Breeze.Utils.MaterialUtil;
import com.Acrobot.Breeze.Utils.StringUtil;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -33,7 +35,27 @@ public class MaterialTest {
continue; continue;
} }
String shortenedName = MaterialUtil.getShortenedName(material.toString(), MaterialUtil.MAXIMUM_SIGN_WIDTH); String shortenedName = MaterialUtil.getShortenedName(material.toString(), MaterialUtil.MAXIMUM_SIGN_WIDTH);
assertSame(material, MaterialUtil.getMaterial(shortenedName)); assertSame(shortenedName + " did not produce " + material, material, MaterialUtil.getMaterial(shortenedName));
} }
} }
@Test
public void testCodesWithMeta() {
int maxWidth = MaterialUtil.MAXIMUM_SIGN_WIDTH - StringUtil.getMinecraftStringWidth("#AAA");
for (Material material : Material.values()) {
if (material.isLegacy()) {
continue;
}
String shortenedName = MaterialUtil.getShortenedName(material.toString(), maxWidth);
assertSame(shortenedName + " did not produce " + material, material, MaterialUtil.getMaterial(shortenedName));
}
}
@Test
public void testCodesWithAndWithoutSpace() {
assertNotNull(MaterialUtil.getMaterial("DiamonPicka"));
assertNotNull(MaterialUtil.getMaterial("Diamon Picka"));
assertNotNull(MaterialUtil.getMaterial("ExpBottle"));
assertNotNull(MaterialUtil.getMaterial("Exp Bottle"));
}
} }