Compare commits

..
Author SHA1 Message Date
Andrew Wong 1327a80050 Move away from interactive messenger 2023-08-28 23:12:10 +10:00
Andrew Wong de3388d4d5 Fix missing argument/lang replacement for the chat prefix 2023-08-28 23:12:08 +10:00
Andrew Wong 594c823cb7 Partially implement argument/language replacements 2023-08-28 23:12:06 +10:00
Andrew Wong 20d7476cab Initial work on adding support for minimessage 2023-08-28 23:11:56 +10:00
Andrew Wong 5619612a6e Update gradle wrapper 2023-08-28 23:10:54 +10:00
Andrew Wong 26ac52cc0a Include plugin adapters in jitpack build 2023-08-28 22:46:14 +10:00
Andrew Wong 5f6bd55afe Don't include nms impls if on jitpack 2023-08-28 22:34:28 +10:00
Andrew Wong aba2e12c5d Don't include nms impls if on jitpack 2023-08-28 22:26:28 +10:00
Andrew Wong 2906de11de Fix merge conflicts 2023-08-28 22:17:42 +10:00
Andrew Wong 436672f85b Don't shadowJar and don't include nms providers if building on jitpack 2023-08-28 22:12:09 +10:00
Andrew Wong 5ba8ed7ad8 Add spotbugs 2023-08-28 20:57:17 +10:00
Andrew Wong 00d97f1beb Update dependencies 2023-08-28 20:36:52 +10:00
Andrew Wong f530fa40af Fix signs not being saved on server stop 2023-08-28 20:14:12 +10:00
Andrew Wong a804df2257 Add jitpack logo 2023-08-09 14:08:12 +10:00
Andrew Wong c96e0b464e More jitpack tests 2023-08-09 13:58:44 +10:00
Andrew Wong 13e52aa4f1 More jitpack tests 2023-08-09 13:55:52 +10:00
Andrew Wong 9aeb531d16 More jitpack tests 2023-08-09 13:52:23 +10:00
Andrew Wong 44f83473d1 Add jitpack.yml 2023-08-09 13:46:05 +10:00
Andrew Wong d7260998f5 Update Guice to 7.0.0 and use JakartaEE annotations 2023-06-12 00:59:16 +10:00
Andrew Wong 638382eacf Snapshot 2.7.13 2023-06-12 00:58:34 +10:00
Andrew Wong e18fe99161 Release 2.7.12 2023-06-12 00:40:46 +10:00
Andrew Wong bdc9e32c13 Add support for 1.20 2023-06-12 00:40:08 +10:00
Andrew Wong 9a46d5ae3b Bump version to 2.7.12-SNAPSHOT 2023-03-29 16:14:26 +11:00
Andrew Wong 6795fc43d9 Release 2.7.11 2023-03-29 16:13:54 +11:00
Andrew Wong dd9bd2c434 Drop support for 1.19.3 and add support for 1.19.4 2023-03-29 16:13:23 +11:00
Andrew Wong 9438166180 Prepare for development 2.7.11 2023-02-27 21:58:41 +11:00
Andrew Wong 9e06b9f268 Release 2.7.10 2023-02-27 21:58:14 +11:00
Andrew Wong 4562bf23f6 Update libs 2023-02-27 21:57:49 +11:00
Andrew Wong 24edb01754 Refactor GeneralRegion#getOwner, #isOwner and #setOwner 2023-02-27 21:46:59 +11:00
Andrew Wong 2739bf6ad4 Fix javadocs for IFileManager 2023-02-27 21:41:28 +11:00
Andrew Wong 2ce84186ab Use Server#getOfflinePlayer in PlayerOfflinePlayerHelper 2023-02-27 21:39:48 +11:00
Andrew Wong 279fa001ea Only support 1.19.3 2023-02-27 11:56:41 +11:00
Andrew Wong b56d5320a6 Add support for 1.19.3 2022-12-15 16:25:06 +08:00
Andrew Wong 30665908b9 Bump to 2.7.10-SNAPSHOT 2022-12-09 16:53:03 +08:00
Andrew Wong 0fd384833a Bump version to 2.7.9 release 2022-12-09 16:45:57 +08:00
Andrew Wong 09634de4bf Fix BukkitSchedulerExecutor not having an injectable ctor 2022-12-07 12:13:34 +08:00
Andrew Wong 7fafee82a7 Minor cleanup 2022-12-07 11:59:37 +08:00
Andrew Wong 2899b2978c Rewrite SetownerCommand 2022-12-07 11:56:50 +08:00
Andrew Wong 7afcba79c9 Rewrite SetlandlordCommand 2022-12-07 11:53:44 +08:00
Andrew Wong 72084b38f8 Use OfflinePlayerHelper in MeCommand 2022-12-07 11:49:01 +08:00
Andrew Wong 6a291feedd Rewrite DelFriendCommand 2022-12-07 11:45:16 +08:00
Andrew Wong b85566882c Rewrite AddfriendCommand 2022-12-07 11:39:09 +08:00
Andrew Wong c14407e14b Add PaperPlatform 2022-12-07 01:23:12 +08:00
Andrew Wong ae9bd60d1b Fix typo 2022-09-14 21:25:04 +08:00
Andrew Wong c689c2fae0 Fix dependency versions for WG and WE, and add a section for FAWE 2022-09-14 21:24:48 +08:00
Andrew Wong 728cfb6dfa Add a compiling.md 2022-09-14 21:16:03 +08:00
Andrew Wong 3abb6c06e4 Update readme 2022-09-14 20:56:00 +08:00
Andrew Wong 51c11738af Prepare for snapshot development 2022-09-12 22:39:45 +08:00
Andrew Wong 172b5a99f1 Bump version to release 2022-09-12 22:21:28 +08:00
Andrew Wong 4c5d025e54 Add config option to toggle whether entities are forcefully cleared 2022-09-12 22:20:55 +08:00
Andrew Wong e93a2d04de Forcefully remove entities from FAWE/WE regions before schematic pasting 2022-09-12 22:12:18 +08:00
Andrew Wong af5980d555 Change the messages for update checking and report file manager loading failures 2022-09-12 17:58:09 +08:00
Andrew Wong 3f7249f4a1 Merge pull request #13 from cleonyc/dev/bleeding
Allow unrented event to use placeholders
2022-09-12 17:39:56 +08:00
Andrew Wong 73c6fc858e Update the logging methods to invoke conditionally 2022-09-12 17:37:47 +08:00
Andrew Wong 360c3e7e21 Use Suppliers when logging in the plugin adapters 2022-09-12 17:35:55 +08:00
Andrew Wong 73d0115b78 Actually fix the FAWE and WE handlers when pasting 2022-09-12 17:25:27 +08:00
Andrew Wong baf7eaabf1 Bump versions of WE and FAWE in the libs.toml 2022-09-11 00:50:36 +08:00
Andrew Wong ca8c0487a8 Fix origin setting so that the blocks paste properly 2022-09-11 00:44:47 +08:00
Andrew Wong 24156a03c4 Rewrite region saving and restoring in the adapters 2022-09-10 23:14:28 +08:00
Andrew Wong ec677766d2 Attempt to prevent duplicate signs from loading 2022-09-10 19:27:03 +08:00
Andrew Wong ef6c8fbf9d Only remove leashes, paintings and item frames 2022-09-10 17:54:48 +08:00
Andrew Wong 4f52fe9e6e Forcefully remvoe Entities in the region before pasting 2022-09-10 17:43:07 +08:00
Andrew Wong 3ebe5a6fa9 Use the WG converter to convert WG regions to WE ones 2022-09-10 17:02:29 +08:00
Andrew Wong 3cebe5d2c7 Fix NPE with sign deletion after worlededit operations 2022-09-10 14:59:35 +08:00
Andrew Wong c76415c4f3 Fix NPE when tabbing region restore 2022-09-10 12:47:45 +08:00
Andrew Wong b7bc02aef3 Compile against 1.19.2 and update the gradle wrapper 2022-09-07 15:31:33 +08:00
Cleo d75094e191 Remove unrenting section in eventCommandProfile 2022-07-07 13:51:02 -04:00
Cleo 4045914b48 Fix placeholders in unrented command event [#11] 2022-07-07 13:50:04 -04:00
Andrew Wong 1fac002ee0 Remove the Bukkit113 module and update the versioning url 2022-06-20 19:57:22 +08:00
Andrew Wong 64301e74e0 Bump version 2022-06-20 19:27:28 +08:00
Andrew Wong 5a5a7fc5ef Bump version 2022-06-20 19:25:42 +08:00
Andrew Wong b928caf0c2 Bump worldedit 2022-06-20 19:19:49 +08:00
Andrew Wong 111f629787 Update dependencies and add adapter for 1.19 2022-06-11 00:59:19 +08:00
Andrew Wong 718474ca95 Update gradle wrapper 2022-06-10 23:37:12 +08:00
Andrew Wong 93b9df3e54 Update gradle wrapper to 7.4.1 2022-03-24 18:48:03 +08:00
Andrew Wong bad983a0b8 Update worldedit and fawe adapters 2022-03-24 18:47:02 +08:00
Andrew Wong e2c8b06b68 Bump dependency versions 2022-03-24 16:30:02 +08:00
Andrew Wong 7249e39c02 bump version 2022-03-24 16:27:49 +08:00
Andrew Wong 9d9a58a170 bump to 2.7.6 2022-03-24 16:24:56 +08:00
Andrew Wong e64e884c2a Updates to the fawe module 2022-03-05 00:00:34 +08:00
Andrew Wong 6ad4ff9deb Shade in the provider for 1.18.1 2022-03-05 00:00:23 +08:00
Andrew Wong eec9806492 Add back support for 1.18.1 2022-03-04 23:08:43 +08:00
Andrew Wong 43b2ae8278 Fix bad website in plugin.yml 2022-03-04 22:54:40 +08:00
Andrew Wong 6d5a92e389 1.18.2 2022-03-04 22:48:39 +08:00
Andrew Wong 7d7a62ffa0 Bump gradle wrapper to 7.4 2022-03-03 17:41:52 +08:00
md5sha256 44456d87b6 Fix the sign error file extension 2022-01-25 05:49:47 +08:00
md5sha256 d92c710b27 bump version to snapshot 2022-01-25 05:49:35 +08:00
md5sha256 3dceaf98cd Bump version to 2.7.5 2022-01-25 05:35:08 +08:00
md5sha256 33c29a3756 Fix the fawe integration and do operations async when possible 2022-01-25 05:34:34 +08:00
md5sha256 771734360f add workflow 2022-01-16 23:24:28 +08:00
md5sha256 539e908d32 Initial work on fixing the deadlock in the fawe module 2022-01-16 21:45:11 +08:00
md5sha256 088471aa3c more project restructuring 2022-01-16 21:28:28 +08:00
md5sha256 5a95c95670 Fix gitignore and restructure project 2022-01-16 20:58:44 +08:00
md5sha256 5a5d627d51 Fix publishing 2022-01-16 15:26:54 +08:00
md5sha256 9b5434a4b2 Add type safe project acessors and version catalog 2022-01-16 14:40:19 +08:00
md5sha256 e13717950b Kotlin dsl 2022-01-16 12:05:39 +08:00
md5sha256 03ed2803bd Bump version to 2.7.3 2022-01-15 07:38:40 +08:00
md5sha256 b3f1225934 Deprecate the FAWE WG handler and fix fawe version parsing 2022-01-15 07:36:43 +08:00
md5sha256 68651899b7 Update the fawe module 2022-01-10 22:59:03 +08:00
md5sha256 4a7a4ca4ee Fix reobf 2022-01-10 22:34:35 +08:00
md5sha256 0d272c8fe9 Publish nms provider poms 2021-12-05 23:07:05 +08:00
md5sha256 6b2c905939 Parallel builds 2021-12-05 22:57:17 +08:00
md5sha256 7ab782799b Fix maven publish tasks 2021-12-05 22:57:03 +08:00
md5sha256 bea82eae88 Fix maven publish tasks 2021-12-05 22:56:37 +08:00
md5sha256 4ac0e045bd Add maven publish task 2021-12-05 18:22:41 +08:00
md5sha256 e5061b218e Java 17 and 1.18 2021-12-01 17:59:17 +08:00
md5sha256 40fdfba47d Fix silly typo in the buildscript 2021-11-18 22:16:36 +08:00
md5sha256 e9260cede6 Bump gradle wrapper version to 7.3 2021-11-18 22:14:56 +08:00
md5sha256 bb297c9ff1 Bump version 2021-11-16 15:28:14 +08:00
Andrew Wong 666742ad56 Merge pull request #2 from md5sha256/feature/purge 2021-11-16 15:25:54 +08:00
md5sha256 36eb8c3a5e Fix imports 2021-11-16 15:25:01 +08:00
Olzie-12 06a9f7b28e file management 2021-11-14 14:41:41 +00:00
md5sha256 e101780420 Fix compat for file manager 2021-11-11 13:31:09 +08:00
md5sha256 6abe99e807 Log sign warnings to the sign error logger 2021-11-09 19:06:02 +08:00
md5sha256 a1d18521ee Introduce interface for FileManager 2021-10-11 00:00:57 +08:00
md5sha256 363b416c59 Retain compat for FileManager 2021-10-10 03:48:12 +08:00
md5sha256 7bf516108b Rename AreaShop again and fix teleport feature 2021-10-09 22:22:53 +08:00
md5sha256 7567cb5fe4 Cleanup global features 2021-10-09 21:33:19 +08:00
md5sha256 d2ca54e8ea Fix signs not updating 2021-10-09 21:03:25 +08:00
md5sha256 e47cda4254 Fix plugin.yml 2021-10-09 17:44:01 +08:00
md5sha256 98d4c6a778 Fix relocations and cleanup DI for Economy and Permission 2021-10-09 17:40:10 +08:00
md5sha256 2a44dd29cc Fix relocations 2021-10-09 17:29:36 +08:00
md5sha256 c1665c33ff Finish cleaning up DI 2021-10-09 17:24:28 +08:00
md5sha256 2cdab9b6db Initial work on guice 2021-10-09 13:27:46 +08:00
md5sha256 72faf76619 Fix ClassCastException in RegionSign 2021-10-09 13:27:28 +08:00
md5sha256 ad66ce00b1 Actually register the sign listener 2021-10-09 02:00:38 +08:00
md5sha256 edc7126b87 Performance improvements to FileManager and various commands 2021-10-09 01:02:00 +08:00
md5sha256 a7f0ac248c Initial work on sign refactoring 2021-10-08 17:44:43 +08:00
md5sha256 675dee2cd3 Add sign error logger 2021-10-07 03:12:45 +08:00
md5sha256 c7226750ab Don't check for WG v5 2021-10-07 01:36:36 +08:00
md5sha256 2ecbb61053 Performance improvements for the teleport feature 2021-10-07 01:34:18 +08:00
md5sha256 fc132558a3 Add a BlockBehaviourHelper 2021-10-07 01:24:20 +08:00
md5sha256 b172413b62 Minor performance improvements 2021-10-07 00:11:09 +08:00
md5sha256 dcbb1f5896 Fix buildscripts and Java 16 support 2021-10-06 23:59:23 +08:00
md5sha256 6e3855c0c3 Remove legacy modules, remove use of ScriptEngine 2021-10-06 23:46:58 +08:00
md5sha256 27991d17a2 Gradle 2021-10-06 23:39:57 +08:00
md5sha256 159221d53d Initial work on updating to 1.17 2021-10-05 23:45:25 +08:00
dependabot[bot] 557d11dfb4 Bump checkstyle from 8.21 to 8.29
Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 8.21 to 8.29.
- [Release notes](https://github.com/checkstyle/checkstyle/releases)
- [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.21...checkstyle-8.29)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-26 11:02:43 +01:00
Thijs Wiefferink b9547e2c67 Remove github action experiment 2019-08-16 18:22:19 +02:00
Thijs Wiefferink 221785ae55 Add test workflow maven build 2019-08-16 17:42:22 +02:00
Thijs Wiefferink 0d04676bce Fix including bukkit into the AreaShop jar 2019-06-23 22:27:35 +02:00
Thijs Wiefferink c8cd68c17b Fixup javadoc build 2019-06-23 22:07:34 +02:00
Thijs Wiefferink 539887449c pom.xml: build with craftbukkit 1.13 2019-06-23 21:59:04 +02:00
Thijs Wiefferink 4cda8a9292 Fix sign handling for 1.14
- Setup modules to load different classes for different Bukkit/Spigot versions
- Create BukkitHandler to get/set the sign facing and get the block it is attached to
- Fix `/as addsign` to use new sign methods
- Fix sign updates to use new sign methods
- Implement full forwards and backwards compatibility of the saved sign material type
    - On Bukkit 1.12 and lower all special wood types are translated to the standard sign
    - On Bukkit 1.13 the internal sign name is translated (and special wood types to the standard one)
    - On Bukkit 1.14 the signs of 1.13- are translated to the OAK variants
- Cleanup the .gitignore files inside of the module folders
2019-06-23 21:53:48 +02:00
Thijs Wiefferink fe131dc157 BuyRegion/RentRegion: fix %player% placeholders for selling/unrenting confirmation messages 2019-06-16 21:40:58 +02:00
Thijs Wiefferink 41ced9ee6c Update checkstyle from 7.6 to 8.21 2019-06-01 17:08:30 +02:00
Thijs Wiefferink 03de0ff252 default.yml: fix spelling mistake 2019-06-01 16:24:15 +02:00
Thijs Wiefferink b057271394 Fix permission check of /as rent, fix 'already bought' check of /as buy 2019-02-20 19:33:37 +01:00
Thijs Wiefferink d7cdff0b22 /as setteleport: fix check that hides it from /as help 2019-02-08 22:29:57 +01:00
Thijs Wiefferink 1035b6b41b Move event commands to a feature class, add two events
- Move eventCommandProfile code to a separate class
- Add AddingRegionEvent
- Add DeletingRegionEvent
- Cleanup a couple of duplicate code paths (merge RentRegion/BuyRegion branches)
2019-01-28 23:37:43 +01:00
Thijs Wiefferink e0bad7397c Refactoring based on SonarLint and Intellij IDEA inspections 2019-01-26 21:23:49 +01:00
Thijs Wiefferink b7124e8cdd Remove mcstats.org metrics class, I don't believe they will be online again 2019-01-26 20:11:26 +01:00
Thijs Wiefferink f0952c32c9 Update issue templates
Add 'NEW' label to issues created using the template
2019-01-13 22:26:54 +01:00
Thijs Wiefferink d3dfa86a65 Set version to 2.6.0 2019-01-13 17:34:50 +01:00
Thijs Wiefferink 8f73b8af3c documentation/release.md: add instructions for releasing a new version 2019-01-13 17:34:37 +01:00
Thijs Wiefferink 51b24c74b4 langHeader.yml: add the template used to generate the language file headers to the repository 2019-01-13 17:33:54 +01:00
Thijs Wiefferink 9beb1eb61b Move the changelog from the wiki to the repository 2019-01-13 17:33:09 +01:00
Thijs Wiefferink de5fdafca0 Pull translations from Transifex 2019-01-08 21:18:19 +01:00
Thijs Wiefferink 0aea914d20 Add FastAsyncWorldEdit support on Spigot 1.13
- FastAsyncWorldEdit now supported on Spigot 1.13
- Improved support for the beta-01 versions of WorldGuard and WorldEdit
- Resolves #436
2019-01-08 00:19:48 +01:00
Thijs Wiefferink b4efe7d5bf Add default labels to issue templates 2019-01-05 16:27:05 +01:00
Thijs Wiefferink ec7983e126 Fix handling of sign updates at places where they will pop off immediately
1. When placing back a missing sign it could be placed in the middle of the air
2. BlockPhysicsEvent would fire immediately (synchronously) breaking the sign
3. Sign update would break because the sign has not actually been placed
2019-01-01 19:32:34 +01:00
Thijs Wiefferink e45c353030 Fix detecting region by location when using WorldEdit 7 beta-2 or later
- The WorldEdit 7 beta-2 handler was inheriting a function from the handler of older version that uses the old Vector class
- Fixes #466
- Fixes #467
2019-01-01 19:30:16 +01:00
Thijs Wiefferink 44b1cb0fc8 /as add: fix setting player as landlord when he is an owner of the region
- Fixes #427
- Adds a debug message to check the behavior easily in the future
2018-12-28 17:11:48 +01:00
Thijs Wiefferink c7299fa56c Update Discord image to a widget image 2018-12-27 23:14:56 +01:00
pmme 12bf467714 Simplify WorldEdit 7 flushQueue vs. flushSession 2018-12-27 22:47:14 +01:00
Thijs Wiefferink 5b327db316 signs: instead of cancelling indirect break, remove the sign to prevent lagg 2018-12-27 19:44:30 +01:00
Thijs Wiefferink b4f33589a1 Cleanup old directory 2018-12-27 17:45:19 +01:00
Thijs Wiefferink 530c74ccd2 Add support for the WorldEdit vector refactor update
- Add WorldEdit and WorldGuard module that supports the new vector system
- WorldGuard/WordEdit dependencies are in the repository itself to make builds more reliable
- Change code that uses WorldEdit vectors to use Spigot vectors
- Rename all modules to areashop-[worldguard|worldedit]-<version>
- Change maven versions of internal modules to 'latest' to be less confusing
2018-12-27 17:42:24 +01:00
Thijs Wiefferink fa4857cbce Simplify code in the handler for WorldEdit 7 2018-12-27 15:00:53 +01:00
Thijs Wiefferink 93fd4e96cb Fix restoring schematics for the latest WorldEdit dev builds
- Fixes #447
- Thanks pmme and x1p for reporting the issue!
2018-10-28 17:58:09 +01:00
Thijs Wiefferink a6863ae782 Also put the Vault dependency into /dependencies
- Now it should be easy again to built AreaShop yourself, only need to run BuildTools for 1.13.0 and it should work
2018-10-28 01:15:12 +02:00
Thijs Wiefferink e5e2c4ca08 Fixup dependencies, use Bukkit/CraftBukkit 1.7.9 instead of 1.8
- 1.7.9 is the lowest supported version, so it is good to use that as base
2018-10-28 00:53:30 +02:00
Thijs Wiefferink b0341e2a3e Fix compatibility with 1.13 Material class
- Fixes sign handling to properly detect and store signs, fixes #440
- Fixes safe teleporting (also improves block detection, making it even safer)
2018-10-28 00:12:24 +02:00
Thijs Wiefferink c8e1ebb068 Change WorldGuard/WorldEdit/Bukkit dependencies to file links
- Fixes the issue that olders WorldEdit and Bukkit versions are not available on Maven anymore
- This speeds up the build a lot
2018-10-28 00:00:41 +02:00
Thijs Wiefferink 39c8eb5f73 Fix tab complete types for /as setduration 2018-10-22 23:44:31 +02:00
Thijs Wiefferink 2483cf1614 Try to add compatibility with the FAWE schematic format 2018-10-21 21:59:45 +02:00
Thijs Wiefferink 769f8ee4ca Set version to 2.5.0, ready for release! 2018-10-20 21:58:14 +02:00
Thijs Wiefferink 2c04b87a9f Add the latest Transifex translations for DE, ES and FR 2018-10-20 21:57:05 +02:00
Thijs Wiefferink 8ce1ed0264 Fix showing /as del and /as add in /help for landlords 2018-10-20 18:36:51 +02:00
Thijs Wiefferink 035f8e1932 Possibly fix default value for areashop.limits.default (documentation is inconsistent for not op) 2018-10-20 18:36:51 +02:00
Thijs Wiefferink 4d3482994f Smaller discord image 2018-10-20 15:53:14 +02:00
Thijs Wiefferink 45f1c510e5 Add link to the AreaShop Discord 2018-10-20 15:49:36 +02:00
Thijs Wiefferink 5c13223b9e Fix for 1.13.1, depend on WorldGuard and WorldEdit 7 beta-01 2018-10-20 14:54:52 +02:00
Thijs Wiefferink 8c47ae9b3c Add README.md files in a couple of packages, closes #128 2018-08-12 16:41:03 +02:00
Thijs Wiefferink 28d03a6a3a Small fixes suggested by Intellij IDEA 2018-08-12 16:23:43 +02:00
Thijs Wiefferink aa585e9b11 Deal with empty date/time settings (handle as 'unlimited'/'disabled')
- Fixes #412
2018-08-12 16:17:02 +02:00
Thijs Wiefferink 36d2ce60b9 Disable copying entities (WorldEdit 7), and enable removing entities (WorldEdit 6)
- Fixes security problem of #273:
    - Throw item into a region
    - Rent/buy the region -> it gets saved as schematic
    - Pick up items
    - Unrent/sell the region -> items appear again
    - Pick up second set of items, they got duplicated
2018-08-11 22:31:15 +02:00
Thijs Wiefferink bdea9448a4 Update WorldEdit 7 to build 3817, update WorldGuard 7 to build 1735 2018-08-11 22:19:25 +02:00
Thijs Wiefferink bd2f40f5c8 Use new schematic format when using WorldEdit 7 2018-08-11 22:18:48 +02:00
Thijs Wiefferink 9e908cee73 Fix getting region by location for WorldGuard 7 2018-08-11 22:17:13 +02:00
Thijs Wiefferink 0d326d973d Fix saving/restoring schematics with WorldEdit 7 2018-08-05 12:23:32 +02:00
Thijs Wiefferink 27eaa27765 Add WorldGuard 7 and WorldEdit 7 support, now 1.13 works
- Closes #411
2018-08-05 00:34:50 +02:00
Thijs Wiefferink fb35153eb2 Pull translations from Transifex 2018-07-22 17:52:25 +02:00
Thijs Wiefferink aec6295516 Remove dependency on javafx 2018-06-17 17:14:23 +02:00
Thijs Wiefferink 38cd77e487 Fix compilation error in the import command 2018-06-01 19:54:02 +02:00
Thijs Wiefferink 6b9866a4c4 Ignore hidden files in the /regions directory (like .DS_Store) 2018-05-29 22:58:15 +02:00
Thijs Wiefferink 5aceb81cfd Import: setup permanent region settings when not rentable and buyable 2018-05-29 22:21:43 +02:00
Thijs Wiefferink 50f6bce64e Fix region limits, thanks @ZachryWilsn for reporting! 2018-05-29 21:59:55 +02:00
Thijs Wiefferink f713edc5b5 Disable moneyback message in /as info if sellDisabled is true 2018-05-26 01:33:23 +02:00
Thijs Wiefferink 012920a51c Cleanups because of Intellij IDEA inspections 2018-05-23 23:46:57 +02:00
Thijs Wiefferink 6154454f96 Indicate if a region has already been added with another world
- Closes #287
2018-05-23 22:53:07 +02:00
Thijs Wiefferink e34daeb5ff Update readme to use issue template selection link 2018-05-23 22:38:12 +02:00
Thijs Wiefferink 0a2dc22c59 Setup issue templates 2018-05-23 22:29:29 +02:00
Thijs Wiefferink 2482bd88c2 Add option to ignore regions for limit calculation
Set `countForLimit` to `false` in default.yml, a group or the region file to ignore it for limits
2018-05-23 22:13:42 +02:00
Thijs Wiefferink e86281ab28 Safer sign update, don't assume location/world still exists 2018-05-12 14:41:45 +02:00
Thijs Wiefferink 5a9ca74b75 Add option to disable selling and reselling your own regions 2018-05-10 20:07:10 +02:00
Thijs Wiefferink d301be2304 Add licence, issues and CI to the root pom file 2018-04-03 22:44:16 +02:00
Thijs Wiefferink a23c6fa66f Hide progress messages of region update, speed up region updates 2018-04-02 20:05:38 +02:00
Thijs Wiefferink cd63517041 Fix list of region names for error indicating missing worlds 2018-04-02 20:00:22 +02:00
Thijs Wiefferink e3043c476b Fix available languages link 2018-04-02 12:10:34 +02:00
Thijs Wiefferink 3be7573863 Parse currency prefix and suffix with InteractiveMessenger
- Allows proper colors and escaping
2018-03-09 11:50:08 +01:00
Thijs Wiefferink 51161fffee Remove unused chunkunload event 2018-03-09 11:08:15 +01:00
Thijs Wiefferink c82067ad0b Add back Paypal donate button 2018-02-17 19:04:18 +01:00
Thijs Wiefferink 46dbb8d22c More customizable metric suffixes, better currency formatting
- 'metricSymbols' section in 'config.yml' to customize the used suffixes for large numbers
- Remove trailing zeros from numbers with metric suffix
- Properly handle rounding of 1.235 to 1.24 (different double to BigDecimal conversion)
- Closes #373
2018-02-12 14:44:48 +01:00
Thijs Wiefferink cace086466 Add UTF8 awareness flag in plugin.yml
- I think it only does something if you use JavaPlugin#getConfig(), but it does not harm either
2018-02-12 12:49:04 +01:00
Thijs Wiefferink 06a716b1fd Make %uuid% available to schematics when selling a region
- Schematic save/restore would happen after removing the owner, which means %uuid% would be null
- Unrenting already does the save/restore before removing the renter, so now it is more consistent
- Thanks to Behoston on Github for reporting the issue
- Closes #383
2018-02-01 13:59:48 +01:00
Thijs Wiefferink 4530717045 Command confirmation for /as import
- Can also be used for all WordEdit based add/delete actions in the future
2018-01-26 16:16:11 +01:00
Thijs Wiefferink 741bbd879f Fix hashCode() of RegionSign 2018-01-26 14:33:27 +01:00
Thijs Wiefferink 8474edd29d Save region name as given to /as add and /as stack
- Previously the WorldGuard region id was used, converting the name to lowercase
2018-01-26 13:55:41 +01:00
Thijs Wiefferink d4df691166 FIx usage of > in javadocs 2018-01-26 12:15:10 +01:00
Thijs Wiefferink 3e247e6cb1 Move sign listeners into SignsFeature
- Fix removing signs not removing it from the signsByChunk map
2018-01-26 12:08:22 +01:00
Thijs Wiefferink f3527134e8 Prevent chunk loading by sign updates
- When sign needs update, only do it if the chunk is already loaded
- If a chunk gets loaded (by a player or something else), update any signs in that chunk
- Improve some javadocs, let checkstyle require @param, @throws and @returns
2018-01-22 18:12:15 +01:00
Thijs Wiefferink 0e14b121b8 InfoCommand: fix showing list of signs (was only showing the first one) 2018-01-22 18:08:16 +01:00
Thijs Wiefferink 8cc632fc05 Pull latest translations from Transifex (automated script) 2018-01-22 16:31:03 +01:00
Thijs Wiefferink 77c1b3f288 Fix error in help message of the import command 2018-01-22 16:10:02 +01:00
Thijs Wiefferink 21339cbf7c Fix incorrect message reference for /as message 2018-01-22 16:07:52 +01:00
Thijs Wiefferink 0351188671 Update live support link 2018-01-21 22:10:02 +01:00
Thijs Wiefferink 47e1d80821 Add live support chat link to 99messages 2018-01-18 16:16:40 +01:00
Thijs Wiefferink 1d0472b052 Improve feedback when placing [as] sign for region that is not added 2017-12-22 12:25:30 +01:00
Thijs Wiefferink 22dcfc76af Fix confirmation messages of /as del (failed refactor)
Thanks Noudux for reporting it!
2017-12-21 18:09:55 +01:00
Thijs Wiefferink 6b59176048 Fix startup error: call init method of the scheduling library 2017-11-27 10:02:51 +01:00
Thijs Wiefferink 5dc4d11408 Fix missing spaces in /as message messages 2017-11-26 23:49:42 +01:00
Thijs Wiefferink 00bd305381 Use Map#putIfAbsent where applicable 2017-11-26 16:01:05 +01:00
Thijs Wiefferink 9daa50b23b Use BukkitDo scheduling library
- Much cleaner code for scheduling tasks,
- Makes proper use of Java 8 functionality
2017-11-26 15:47:51 +01:00
Thijs Wiefferink 33b73a0369 Remove Paypal donate button 2017-11-25 17:07:54 +01:00
Thijs Wiefferink 591540af30 Partially fix problem with importing regions from RegionForSale
- When checking the type of a region AreaShop would only look at the settings of the region, not the settings of the world, parent regions and config.yml
- Checking parent regions for these settings should still be added (requires bigger changes)
2017-11-23 22:54:55 +01:00
Thijs Wiefferink 4e1e50e993 Fix /as linksigns <profile> for configs without signProfiles
The `signProfiles` section is not in the default config.yml file anymore, so getting that section can return null
2017-11-20 09:19:35 +01:00
Thijs Wiefferink 9a52ca3d9b Add missing @return Javadoc for RegionGroup#getAutoRegions() 2017-11-10 00:04:39 +01:00
Thijs Wiefferink 858b019806 Add option to teleport X blocks away from the sign
- Implements #364
- Option `general.teleportSignDistance` to `default.yml` to set the number of blocks to teleport away from the sign
- Changes the pitch/yaw of the player so that he faces the sign
- Handles wall and floor signs in any direction
- Improve SignsFeature code structure
2017-11-09 23:56:18 +01:00
Thijs Wiefferink b1dfe93088 Add RegionForSale import, rental auto-extend and other things
- `/as import RegionForSale`, as requested in #98:
    - Imports all regions from RegionForSale
    - Converts almost all settings to their AreaShop equivalent
    - Handles parent regions
- Add `rent.autoExtend` for use with rental regions to automatically extend the rent when the time is out, start of #29, still need the command
    - Vault compatible permissions plugin is required to let this work while the player is offline
    - All permissions, limits and the player balance are checked as during a normal rent
- Add option to region groups to automatically add all regions of a certain world
- BREAKING: Region events now use OfflinePlayer instead of Player
- Automatically import existing region owners/members when adding a region to AreaShop

There are still a couple of things missing in the import and it needs to be tested a lot.
2017-10-20 13:41:30 +02:00
Thijs Wiefferink 0bf2314f2a Add licence (as already listed on BukkitDev) 2017-10-16 11:56:04 +02:00
Thijs Wiefferink 33ddb8ae88 Fix %volume% for polygon regions
- WorldGuard does not implement volume() for Polygon regions, always returns 0
- Volume result is cached, calculating it for polygon regions could be slow
2017-10-05 16:53:42 +02:00
Thijs Wiefferink 234e4b6261 Prevent indirectly breaking signs with non-solid blocks
- Fixes #358
2017-10-05 13:43:27 +02:00
Thijs Wiefferink ed2ffaeaa3 Move base command to EN.yml for /as info paging 2017-10-05 13:15:51 +02:00
Thijs Wiefferink e073438862 Add %volume% variable to get the number of blocks in a region
- Works properly for polygon regions
2017-10-05 12:20:03 +02:00
Thijs Wiefferink 07194f07da Add region masking to schematic restore
- When restoring a schematic the paste is masked to the region blocks, so that only blocks that are actually inside the region are pasted (necessary for polygon regions)
- Closes #20
2017-10-01 19:49:05 +02:00
Thijs Wiefferink dfa3e0be66 Checkstyle fix 2017-09-04 17:09:31 +02:00
Thijs Wiefferink 8eef029400 Fix %month% variable using 0-11 instead of 1-12
- Fixes #354, thanks skippyaut
2017-09-04 16:58:39 +02:00
Thijs Wiefferink c8d869ce15 Fix usage of teleport location set by '/as settp'
- It was accidentally trying to get the teleport location from the plugin config file instead of the region config file.
- Also added the possibility to set the teleport location in groups and default.yml
2017-08-05 20:25:06 +02:00
Thijs Wiefferink 053d75181d Update to InteractiveMessenger 1.1-SNAPSHOT
- Ensures AreaShop is rebuild when InteractiveMessenger updates
- Fixes crash bug that was in InteractiveMessenger
- Fixes #334
2017-07-30 17:25:49 +02:00
Thijs Wiefferink f0128c221b Update README.md 2017-06-23 14:59:16 +02:00
Thijs Wiefferink ce6c01c5d0 Add Transifex translation stats image 2017-06-23 14:54:19 +02:00
Thijs Wiefferink b835c4dded Only reset WorldGuard flag/groupFlag if not already empty 2017-06-12 22:05:09 +02:00
Thijs Wiefferink 07e6782714 Fix /as setowner for regions that are not rented yet
Add a rental period to the time when setting owner of a rental region that is not rented yet, otherwise it would get expired immediately
2017-06-12 21:58:42 +02:00
Thijs Wiefferink 4002956aeb Fix javadoc comments 2017-06-12 20:42:36 +02:00
Thijs Wiefferink 0b96b9cb42 Github update checker, Java 8
- Add Github update checker, closes #201, it is implemented to allow reuse by other plugins, it will probably move to a separate repository later
- Switch to Java 8, if people did not update already they probably never will
- Add used Bukkit version as property, use that in all pom files
- Add Task class with methods to easily use BukkitRunnable (not all code is migrated to this pattern yet)
2017-06-12 20:39:28 +02:00
Thijs Wiefferink 0c192c2444 Cleanup setOwners/setMembers, only edit WorldGuard region if different
- Improve code layout for changing owners and members of WorldGuard regions
- Only edit flags and other WorldGuard region settings if they are different than the current option, this prevents useless saves in WorldGuard (which shows a message in console)
2017-06-12 20:39:28 +02:00
Thijs Wiefferink 2381aaeacc Remove link to Shop plugin because it is not fixed yet 2017-04-30 14:15:35 +02:00
Thijs Wiefferink 906b331758 Fix line endings in config.yml 2017-04-20 23:35:49 +02:00
Thijs Wiefferink f0f2e09be0 Always add English time indicators
- Internal timers specified using settings in hiddenConfig.yml use the English words, so if you remove the English words from config.yml timers do not run (regions not expiring, no notifications, no file saving)
- Warn in console when an unknown time specifier is found, might get spammy but at least gets the point across
2017-04-20 23:23:57 +02:00
Thijs Wiefferink 395cc6de07 Fix usage of 'resale' in 'default.yml' for 'flagProfile' section
- 'resale' was used instead of 'resell' (as defined in GeneralRegion#RegionState) in the 'flagProfile' section, causing the flags not getting applied when the region is in reselling state.
- Added fallback to 'resale' section if 'resell' section is not present (to properly read old config.yml/default.yml files)
- Fixed usage of '%resaleprice%' in the resale greeting, should be '%resellprice%'
- Fixes #303, thanks electro2560 for the great bug report!
2017-04-20 15:26:26 +02:00
Thijs Wiefferink 3b9d6187ca Fix checkstyle fail 2017-04-17 21:34:32 +02:00
Thijs Wiefferink 9d1585f513 Fix groups file getting cleared on server start
- Fixes #299
- Because of an earlier change the groups file is loaded before regions are loaded on server start, and region groups remove themselves if there are no valid regions in them anymore.
- Keep groups even though they are empty (so that configuration done in the group is not lost)
2017-04-17 21:32:15 +02:00
Thijs Wiefferink 9d6955039a Fix '/as groupdel' with non-existing group creating an empty one 2017-04-17 21:26:24 +02:00
Thijs Wiefferink 747b47057b Allow # in region name for /as stack to determine counter position 2017-04-11 21:22:35 +02:00
Thijs Wiefferink a973e254e6 Fix '/as addfriend' not allowing you to add players that are playing for the first time
- Fixes #297
2017-04-02 22:34:02 +02:00
Thijs Wiefferink a5105af3ad Add 'use' flag to default.yml again since people mess it up 2017-03-29 20:18:14 +02:00
Thijs Wiefferink 0bb0a05b76 Cleanup checkstyle from pom.xml in AreaShop module 2017-03-29 20:17:42 +02:00
Thijs Wiefferink dad502ec0c Add checkstyle to enforce formatting, change all formatting to comply 2017-03-29 20:15:44 +02:00
Thijs Wiefferink f471b9bbf0 Don't show 'in advance renting:' in '/as info' if maxExtends is zero
- Thanks to R0bstar for noticing this weird weird message combination
2017-03-09 16:14:30 +01:00
Thijs Wiefferink f69d1c41e6 Code cleanup and stricter visibility of methods/variables 2017-03-09 16:01:36 +01:00
Thijs Wiefferink 4ea6f59c49 Remove GeneralRegion#is{Rent|Buy}Region()
- It served no purpose and prevents IDEs from detecting type safety
2017-03-09 15:29:01 +01:00
Thijs Wiefferink 0b7158290f Add some more bStats statistics 2017-03-09 15:09:05 +01:00
Thijs Wiefferink e6757d23de Add bStats analytics
- Add bStats analytics because MCStats is down constantly
- Add custom graphs:
  - Language pie chart
  - Number of registered regions
  - Number of rental regions
  - Number of buy regions
- Closes #287
- In the future MCStats can be removed, for now I would like to keep complete data there for the times it does work
- Could add more graphs in the future for number of friends and things like that
2017-03-02 18:14:07 +01:00
Thijs Wiefferink 7cda35f4da Remove duplicate link in the readme 2017-02-12 20:04:35 +01:00
Thijs Wiefferink ee293d14b9 Fix link to CI in the readme 2017-02-12 20:03:46 +01:00
Thijs Wiefferink 2e0fce4955 Update repository url 2017-02-09 14:59:53 +01:00
Thijs Wiefferink 7afdefc9ed Add missing javadoc to loadFiles methods 2017-02-02 23:03:55 +01:00
Thijs Wiefferink 11f80e7ed1 Start using InteractiveMessenger
- Messaging functionality has been split into the InteractiveMessenger project: https://github.com/NLthijs48/InteractiveMessenger
- Jenkins build server has been changed to publish a Maven repository: http://jenkins.wiefferink.me/plugin/repository/everything
2017-02-02 22:59:00 +01:00
Thijs Wiefferink 40d45c7786 Fix using landlordName setting without using landlord (uuid) setting 2017-02-02 17:20:26 +01:00
Thijs Wiefferink 6b8e161e43 Ensure buy/rent/resell price is never below zero
This prevents problems with negative payment or withdrawal in Vault if input validation of a command is not good enough or if a price has manually been set below zero.
2017-02-02 17:18:26 +01:00
Thijs Wiefferink e1b2f7391c Improve message when you cannot rent/buy a region
The message did not specify which region cannot be rented/bought, which has now been added.
2017-02-02 16:45:15 +01:00
leNic 89ae8b9b4a Fixed reselling with negative prices (#280)
Prevents setting a negative resell price,. Some economy plugins accept a withdraw with a negative number, which has bad consequences (for example EssentialsEco does not accept it).
2017-01-28 20:25:24 +01:00
Thijs Wiefferink fab415bf19 Speed up expiration and expireWarning tasks
The expire task was triggering more often than the expireWarning task, causing lost warnings when warnings with a low time are specified
2017-01-23 14:26:25 +01:00
Thijs Wiefferink c7aa92be81 Indicate in default.yml that expressions can be used for region prices 2017-01-13 19:25:31 +01:00
Thijs Wiefferink 705c8d6891 Update README.md 2017-01-13 18:42:04 +01:00
Thijs Wiefferink 429d87c9ff Improve feature list 2017-01-13 18:41:16 +01:00
Thijs Wiefferink 1fdd8b32cc Add prices depending on a formula
- Price can be specified with an expression (JavaScript syntax)
- You can also use a block of JavaScript code, the value of the last line is used as result (for more complex logic)
- Supported settings: rent.price, buy.price, rent.moneyBack, buy.moneyBack (need more? ask me)
- Before evaluating the expression all region variables as defined in the config.yml file will be replaced (allows you to use %width% etc)
- Added %timesExtended% variable for use in expressions (to know how long the user is renting for)
- '/as setprice' and signs still only allow number input to prevent security problems (this means actual config access is required to do anything of harm with JavaScript)
- Closes #23
2017-01-13 16:56:02 +01:00
Thijs Wiefferink 7900f78a8c Change default WorldGuard flags to include block-place and block-break
Way too many people set the block-break and block-place flags on bigger or even the global region to deny (which is unnecessary and wrong relatively, for global use 'passthrough: deny' instead) so this patches up those mistakes by setting them at the AreaShop region
2017-01-13 16:06:18 +01:00
Thijs Wiefferink 3a65a88849 Fix replacing variables into language variables
- Previously it would insert it as a message, causing the language variable to be split up into multiple parts (which prevents recognizing it later)
- If a Message is marked as inline with Message#inline() it will be inserted without breaking up the message
2016-12-27 16:17:04 +01:00
Thijs Wiefferink 7b491d0389 Lazily instantiate feature classes to improve performance 2016-12-27 16:15:17 +01:00
Thijs Wiefferink fd02c51492 Add setting to prevent cross-world teleportation with '/as find'
- If a region is in another world and 'findCrossWorld' is off, it will be filtered from the results
- 'teleportCrossWorld' will not be checked anymore if the teleport is called from '/as find'
- Resolves  #234
2016-12-26 23:29:40 +01:00
Thijs Wiefferink bb1f592807 Add setting to prevent cross-world teleportation with '/as tp'
Related to #234
2016-12-26 23:20:12 +01:00
Thijs Wiefferink 16c7a044ea Add and make use of escaping in messages
- Add escaping support to FancyMessageFormat: put a backslash before a tag and it will not be interpreted, also works for color codes (&2 and §2) and variables
- Make use of escaping in the Message class to properly handle user input as plaintext (previously user input that got printed back also got interpreted, like colors apply to it)
- All replacements given to Message are escaped, unless you wrap them in a Message before it.
- Replacement limits and infinite replacing detection have changed slightly
2016-12-26 22:56:35 +01:00
Thijs Wiefferink 152f183670 Fix region update after /as reload, cleanup unused code 2016-12-25 23:02:23 +01:00
Thijs Wiefferink 09b6e4c584 Fix string setting as profile in default.yml
- This has been broken in b525c5902c
- Fixes #267
2016-12-25 22:40:11 +01:00
Thijs Wiefferink aeb70196a3 Fix shutdown exception in SignsFeature 2016-12-24 19:59:04 +01:00
Thijs Wiefferink 62148ea577 Move teleportation code to a feature class 2016-12-23 23:25:41 +01:00
Thijs Wiefferink fcc9fb0945 Refactor to introduce Manager and RegionFeature
- Manager to serve as abstract class for managers (like FileManager), implements a lifecycle to allow for shutdown operations (like save all files)
- RegionFeature to server as abstract class for region features
- Improve instantiation and lifecycle of features (global features and region specific ones)
- This is preparation for moving more region functionality into feature specific classes (which makes GeneralRegion smaller and easier to maintain)
2016-12-23 16:03:22 +01:00
Thijs Wiefferink b525c5902c Fix default settings leaking when getting configuration sections
- Default settings and custom settings are in separate YamlConfiguration instances now to prevent leaking
- When getting a configuration section you now know that either it is from the current default.yml file or the default.yml file in the jar (querying settings from the section will always have the same source)
- Fixed a startup null pointer
- Fixed #266
2016-12-23 14:07:26 +01:00
Thijs Wiefferink b18a7cc6d5 Fix [break] in hover lines
Hover lines are still a bit broken when trying to insert complex messages into them, this is hard to do in a proper way though.
2016-12-10 15:19:34 +01:00
Thijs Wiefferink 54b6e84558 File loading improvements to be more robust 2016-12-04 21:14:31 +01:00
Thijs Wiefferink bae3c0a199 Ask for config files in the issue template 2016-12-04 17:56:37 +01:00
Very Evil Olaf 508270a280 Update ISSUE_TEMPLATE.md (#258)
Note that 'latest' is not a correct version
2016-12-04 17:48:36 +01:00
Thijs Wiefferink 87b2cb9ecd Prevent exceptions if a schematicProfile does not exist 2016-12-04 17:31:22 +01:00
Thijs Wiefferink 271b17b6be Set version to 2.4.0 and update language files 2016-12-02 14:37:27 +01:00
Desjardins Jérôme 1dfedea50c Create a permission for teleporting to available regions (#241)
* Update GeneralRegion.java
Add Available abstract function
Check Permission modification

Update BuyRegion.java
Add Available function for selled

Update RentRegion.java
Add Available function for rented

Update TeleportCommand.java
Check new permission

Finish feature teleport available region

* Fix translation
2016-11-19 17:15:23 +01:00
Thijs Wiefferink d9abe2e347 FIx problem with null regionFlags sections 2016-11-14 18:02:59 +01:00
Thijs Wiefferink 1ba2e21ae9 Make profile based settings easier to understand
- Remove 'signProfiles', 'flagProfiles', 'eventCommandProfiles', 'schematicProfiles' and 'expirationWarningProfiles' from the config.yml file
- Add the removed profile contents directly to the setting without the trailing 's' in the default.yml file (this prevents the need to understand the creation of profiles, and then assigning them, taking out a step in the thought process of users)
- Added method in GeneralRegion to get a ConfigurationSection from the region file, group files or default file while also taking into account the translation from a string to a profile that is defined in the config.yml file. This ensures backwards compatibility with current config files (the old style will not be promoted anymore though).
- The 'expirationWarningProfiles' does not have the subkeys 'warnPlayer' and 'commands' anymore, it is just a list of commands now, using the '/areashop message' command to warn a player.
- Add a workaround for the empty sign problem after unrent/sell of a region while having restore enabled, an AsyncWorldEdit plugin installed and signs inside the region (sign will be updated again half a second after unrent/sell).
2016-11-13 00:44:35 +01:00
Thijs Wiefferink fcdfdc6094 Update language files
- Swedish is added
- German, French and Russian are improved
2016-11-07 14:13:03 +01:00
Thijs Wiefferink 8a7e2ba5c2 Indicate limits when a MaxChangedBlocksException is thrown by WorldEdit 2016-11-07 13:52:54 +01:00
Thijs Wiefferink 0950452692 Fix a couple of problems caused by vertical /as stack addition 2016-10-31 21:07:08 +01:00
Thijs Wiefferink 293978f068 Decouple Message class from replacement providers
- Instead of applying replacements by testing for a replacement that has the GeneralRegion type, it now checks for a ReplacementProvider instances (one step closer to making Message independent of AreaShop).
- Replacements are handled one by one with a switch-case instead of getting them from a map, helps performance a lot.
2016-10-25 20:57:58 +02:00
Thijs Wiefferink 5efe14b8e9 Add up and down direction for '/as stack'
Prevents creating regions above and below the world limits, still needs
testing.
Progress for #105
2016-10-24 15:02:49 +02:00
Thijs Wiefferink 839d510151 Switch from Bukkit to Spigot Maven repo since it is down
Also switched from Bukkit 1.7.9 to Bukkit 1.8.0 because that is the lowest version available on the Spigot repo
2016-10-17 23:45:08 +02:00
Thijs Wiefferink 9180d9b8ee Fix small language file issue
When you only have a formatting code before a language variable, the hover/click effects will accidentally be applied to one text part earlier
2016-10-16 18:29:26 +02:00
Thijs Wiefferink 7aa786f4ce Fix default config trying to execute /as message as player
Fixes #221
2016-09-04 22:43:03 +02:00
Thijs Wiefferink 10ea8540e7 Add tab completion for '/as me [player]'
Closes #220
2016-09-04 00:59:04 +02:00
Thijs Wiefferink dba00769f6 Fix problem with line breaks 2016-09-03 20:58:45 +02:00
Thijs Wiefferink 44c70c6a33 Cleanup messages about region that does not exist in WorldGuard 2016-09-03 19:46:05 +02:00
Thijs Wiefferink 570f7b4055 Don't extend rent when using '/as setowner' to set a different owner
Also remove new owner from the friends list.
Fixes #219
2016-09-03 19:19:40 +02:00
Thijs Wiefferink feafa012dd Fix issue with \n on 1.7.x servers
- Each line of a message will now be sent separately instead of using '\n' in the text
- Drastically reduced the number of StringBuilders that are instantiated during message construction
- Fixes #222
2016-09-03 17:22:40 +02:00
Thijs Wiefferink 82b340ce1a Messages: also parse section signs into color tags properly
Fixes line-wrapping removing color in some cases
2016-09-02 21:43:58 +02:00
Thijs Wiefferink 93809f27fc Rename events to be consistent with commands
- AddFriendEvent -> AddedFriendEvent
- DeleteFriendEvent -> DeletedFriendEvent
- RemovedRegionEvent -> DeletedRegionEvent
2016-08-28 22:45:20 +02:00
Thijs Wiefferink f8ed3654fd Fix compilation error 2016-08-28 22:31:06 +02:00
Thijs Wiefferink 58a1afccf3 Invert inheritance of RegionEvent and CancellabelRegionEvent/NotifyRegionEvent 2016-08-28 22:29:45 +02:00
Thijs Wiefferink d9c3573225 Change RegionUpdateEvent to UpdateRegionEvent
This breaks plugins using the event
2016-08-28 22:21:33 +02:00
Thijs Wiefferink 1577443203 Generalize events better
Adds generic classes for events with a region (BuyRegion, RentRegion or GeneralRegion)
2016-08-28 22:11:17 +02:00
Thijs Wiefferink ab7a92c57d Update README.md 2016-08-26 19:49:21 +02:00
Thijs Wiefferink 2e09b0f60f Update README.md 2016-08-26 19:48:59 +02:00
Thijs Wiefferink 6e5c35f95b Add extension plugins section 2016-08-26 19:45:37 +02:00
Thijs Wiefferink 066ceedafd Add 'build' flag by default to prevent inheriting from parent regions. 2016-08-21 22:20:25 +02:00
Thijs Wiefferink c8ebe24bd3 Put break in '/as info' help message in the right spot 2016-08-19 20:52:07 +02:00
Thijs Wiefferink ad7db5ee23 Attempt to fix unable to add new players with '/as addfriend' 2016-08-16 22:43:09 +02:00
Thijs Wiefferink f80a8178ff Fix formatting not clearing on new lines 2016-08-13 22:28:15 +02:00
Thijs Wiefferink 05c8e1a7e4 Make loggin methods more generic 2016-08-10 16:07:39 +02:00
Thijs Wiefferink 9904805310 Improve version detection of WorldGuard
- First detect version 5
- Then detect lower than 6.1.3
- Else check build number:
  - Build number exists and lower than 1672: 6
  - Build number exists and Is 1672: broken
  - Else: 6_1_3
2016-08-09 22:56:21 +02:00
Thijs Wiefferink 0df2f429c0 Only print build number warning if we found it 2016-08-09 22:38:33 +02:00
Thijs Wiefferink b3408a2648 Fix compilation error and WorldGuard version detection 2016-08-09 21:52:53 +02:00
Thijs Wiefferink e1f89e7904 Cleanup some leftover logging in an old style 2016-08-08 22:06:31 +02:00
Thijs Wiefferink 064b129205 Change version to 2.3.2 and update languages
- Russian has been added
- Spelling errors in French have been corrected
2016-08-07 22:21:57 +02:00
Thijs Wiefferink 8111387f32 Improve friend line of /as me, add click hint
Change blue color of hover popup to gold, which will be used as action color
2016-08-07 22:06:09 +02:00
Thijs Wiefferink a4b23bebba Improve line of rented regions in info command 2016-08-07 21:49:44 +02:00
Thijs Wiefferink 7421e685fa Improve robustness of variable replacing in messages
- Once the limit is reached the argument replacer would still continue sometimes (if a the replacement itself triggers the limit and the inserted message increases the size of the message we are building and adds a parameter tag again, like %0%)
- The language tag replacer also stops earlier now, it did not have the above problem though
- Fixed FancyMessageFormat.insertMessage() not checking its parameters
- Message.get() with a limit will now return an empty message if the limit is reached to prevent problems like above.
- Closes #205
2016-08-07 00:09:30 +02:00
Christoph Krüger @Androkai 374b68d8fa Update VaultAPI-repo (#206)
The url of the Vault Maven repository has changed.
2016-08-06 22:36:26 +02:00
Thijs Wiefferink ca7a9664f3 Fix error in info command of rental region 2016-08-02 22:27:55 +02:00
Thijs Wiefferink e957b4fc73 Remove all reflection for sending fancy messages
- Reflection removed
- JSON is now fully escaped instead of lenient escaping
- Sending is now done with /tellraw
2016-08-01 21:31:14 +02:00
Thijs Wiefferink 6493bc4f78 Add empty lines if a page does not fill the screen
Relevant for commands like `/as info all` that can have pages
2016-08-01 21:28:11 +02:00
Thijs Wiefferink 2db1b6a243 Prevent updater throwing errors sometimes when BukkitDev is offline 2016-08-01 20:46:40 +02:00
Thijs Wiefferink 864662a3c4 Add javadocs to limitgroup inner class and setting methods 2016-08-01 20:23:49 +02:00
Thijs Wiefferink 59b960b87c Update languages, set version number to 2.3.1
- Added German translation
- Added French translation
- Added Norwegian translation
2016-08-01 19:36:50 +02:00
Thijs Wiefferink 2de653d73f Add automatic conversion of language files downloaded from Transifex 2016-08-01 18:03:09 +02:00
Thijs Wiefferink 4feee59834 Cleanup info and warning messages. 2016-08-01 16:18:05 +02:00
Thijs Wiefferink 816d136a62 Change version number to 2.3.0, update languages
- Only English and Dutch are available yet
2016-07-24 21:48:39 +02:00
Thijs Wiefferink 121de7dc7e Add build number option to Maven
- Build number option can be used by Jenkins to add the build number
- Build number of soruce on Github will be #CUSTOM to mark custom builds
- Release versions will not contain a build number
- Closes #185
2016-07-24 20:59:25 +02:00
Thijs Wiefferink f7c86881cc Fix the default flagProfiles settings, fix migration issues. 2016-07-24 20:25:58 +02:00
Thijs Wiefferink 351d51b664 Correct message for reselling region entries. 2016-07-23 19:31:45 +02:00
Thijs Wiefferink 5a28476cf0 Add better limits for messages
- Recursive replacements are handled by catching stackoverflow exceptions
- Messages are limited 30000 in length for the produced JSON string since the client cannot handle more
- Error message print out the message key and start of the message
2016-07-23 17:21:08 +02:00
Thijs Wiefferink 8143bdae27 Add pages to the /as info command to prevent crashes
- Output displayed in pages of 18 (+ header and footer it fills the max of 20 you can see without scrolling)
- Footer shows current and max page, and has buttons to go to next/previous
- Commands accept [group] [page] both as optional arguments, works because page is a number (number as group would be weird)
- Entries show basic info about regions (owner/price/duration), and of course can be clicked to show all info
- Made debug command more generic
- Closes #172
2016-07-23 02:57:31 +02:00
Thijs Wiefferink b62744fe10 Add warnings for missing settings in limit groups.
Closes #164
2016-07-21 23:00:19 +02:00
Thijs Wiefferink fe0b63c760 Remove incorrect check 2016-07-17 23:51:24 +02:00
Thijs Wiefferink def774c7a1 Link javadoc of external classes properly
Classes of Bukkit, WorldGuard and WorldEdit that appear in the javadoc now link to the javadoc of them.
2016-06-27 23:38:44 +02:00
Thijs Wiefferink 7c5cb5f5aa Add javadoc to getApplicableRegions methods 2016-06-27 23:16:21 +02:00
Thijs Wiefferink 8af6e1d6f1 Fix small Maven warning 2016-06-25 22:20:21 +02:00
Thijs Wiefferink e0e6c39dae Fix WorldGuard version detection for later stable releases 2016-06-21 22:42:56 +02:00
Thijs Wiefferink dabe46bd47 Move all sign related methods into SignsFeature
- Each GeneralRegion now has a SignsFeature
- Each SignsFeature contains RegionSign instances representing the actual signs
 - The RegionSign class contains methods to update them etc
 - There is one SignsFeature that is not linked to a region, but registered as listener to update the signs at events
2016-06-20 20:16:13 +02:00
Thijs Wiefferink ec478a05d1 Move region friends code to separate Feature class 2016-06-20 18:22:35 +02:00
Thijs Wiefferink e21c0aafde Add events for adding/removing friends
- Cancellable event for adding friends: AddFriendEvent (region, friend and who is trying to add it available)
- Cancellable event for removing friends: DeleteFriendEvent (region, friend and who is trying to delete it available)
- Closes #54
2016-06-20 17:03:12 +02:00
Thijs Wiefferink 52bf88f4a5 Prevent RemovedRegionEvent triggering twice
Add debug logging for RemovedRegionEvent and AddedRegionEvent
2016-06-20 16:32:10 +02:00
Thijs Wiefferink e4de560a65 Prevent Maven warning
It was trying to include the WorldGuard6 handler class twice in the jar file
2016-06-20 16:28:23 +02:00
Thijs Wiefferink 6481da29e4 Add compatibility for WorldGuard build 1674 and up.
- Added a module for WorldGuard 6.1.3 to adapt to changes in flag related methods.
- No compatibility with build 1672, since there is no dependency in the Maven repo of WorldGuard to target this (this is a dev build though so I don't expect people using it long-term)
- Move properties for setting Java version to the parent pom file (other modules inherit them)
- Move repositories to the parent pom file (other modules inherit them)
- Fix AreaShop module not declaring its parent
- Fixes #170
2016-06-18 23:23:22 +02:00
Thijs Wiefferink f287cc91a6 Remove leftover logging 2016-06-13 21:11:00 +02:00
Thijs Wiefferink 95b7b50346 Add development builds and javadocs links 2016-06-13 20:59:08 +02:00
Thijs Wiefferink 21db2ff440 Improve Maven setup for javadoc and source
- Generate a .jar file with the sources if -Dsources is provided
- Generate javadocs if -Djavadoc=/path/to/destination is provided
- Copy final .jar file to directory if -DcopyResult=/path/to/server is provided
- Adding missing javadoc comments
2016-06-13 20:36:17 +02:00
Thijs Wiefferink 36dd978a92 Cleanup the config file and settings
- Moved scheduled task settings to hiddenConfig.yml (settings that are not visible, but still usable)
- Remove 'postCommandErrors' setting
- Also execute commands defined in 'expirationWarningProfiles' when player is not online
- Move around settings in config.yml and improve explanations
- Closes #168
2016-06-13 17:03:20 +02:00
Thijs Wiefferink 6cc3f2a52f Combine all messages about region not registered
And replace mentions of 'this region' with the actual region name.
2016-06-13 14:47:10 +02:00
Thijs Wiefferink 852731b1ad Add action confirmations when using signs
- Confirmations by default for selling/unrenting by signs, not for renting/buying
- Fixed a bug with message composing which would duplicate hover/click parts.
- Added `/as message <player> <message...>` for internal/console use (not shown in help).
- Closes #50
2016-06-12 21:39:12 +02:00
Thijs Wiefferink 30faa77da9 Fix small error in multiple regions found message 2016-06-12 19:38:05 +02:00
Thijs Wiefferink ec57b922b8 Messages: Version fixes, fallback to plain.
- Properly parse NMS version number to support 1.10
- Fallback to plain messages if sending a fancy message breaks (we try again after restart/reload)
2016-06-12 16:48:47 +02:00
Thijs Wiefferink c2bcc6008b Add hover/click to some forgotten mentions of regions 2016-06-12 13:25:55 +02:00
Thijs Wiefferink 72a05bedce Merge personal/all help messages. 2016-06-11 22:40:09 +02:00
Thijs Wiefferink bc73a8b20d Cleanup links to issues 2016-06-11 17:35:00 +02:00
Thijs Wiefferink 832e8218b6 Add issue template for bugs/feature requests 2016-06-11 17:29:47 +02:00
Thijs Wiefferink 6fcb629133 Move to the me.wiefferink package
The nl.evolutioncoding domain is soon not mine anymore, and this new one I do intend on keeping.
2016-06-07 22:39:45 +02:00
Thijs Wiefferink 7d6a1f35c1 Messages: Add hover/click items to region info
- The hover/click parts only appear if you have permission to use the command linked to it
- Red crosses added next to the friends in the friend list to remove them
2016-06-05 17:04:38 +02:00
Thijs Wiefferink 60257d45c2 Messages: Add a bunch of click/hover actions to '/as info region <region>'
- rent/unrent/buy/sell/extend
- teleport, set teleport location
2016-06-04 22:46:10 +02:00
Thijs Wiefferink 2300227d87 Messages: Fix problem with message insertion sometimes missing leftover part 2016-06-04 20:57:14 +02:00
Thijs Wiefferink b1852b10bb Improve /as me, remove /as info player
And change underline to bold for clickable things (underline leaks through in the next line, giving annoying layout issues)
2016-06-02 22:39:30 +02:00
Thijs Wiefferink 88fdb0c3fc Add 'ALL' flagProfiles state that is always applied
And add block-break and block-place by default.
2016-05-31 22:36:18 +02:00
Christoph Krüger 491e55fc77 Change Vault dependency to Maven repo instead of .jar
Use the Maven repository of Vault instead of including a jar file in the repository.
2016-05-28 23:19:23 +02:00
Thijs Wiefferink c7b0a2c663 Let /as help respect the areashop.help permission
Closes #155
2016-05-28 17:04:06 +02:00
Thijs Wiefferink 868f4d6b58 Events: Add refunded money to UnrentedRegionEvent and SoldRegionEvent
Closes #147
2016-05-28 17:00:52 +02:00
Thijs Wiefferink 0d4b034181 Messages: Detect and stop replacement loops. 2016-05-28 16:51:32 +02:00
Thijs Wiefferink 5f20689ba3 Improve language variable argument detection 2016-05-27 21:01:14 +02:00
Thijs Wiefferink 38b658fccf Message changes: click on regions/players
- Click on region name for region details
- Click on player name for regions of the player
- Click on landlord name for regions of the landlord
- Change color codes to things like [blue] instead of &9 (both are supported though)
2016-05-24 23:18:25 +02:00
Thijs Wiefferink 28ed20c0b5 Message system fixes and improved robustness
Hover/click message for '/as info'
2016-05-23 12:16:19 +02:00
Thijs Wiefferink aaff36b2eb Add hover/click support for messages #18
- Language files support a way to add hover tooltips, click commands and click insert commands (using FancyMessageFormat created together with PhoenixIV)
- Language variables support arguments
- Help listing (/as help) uses the new hover/click things, more to come
- See #18 for the TODO list
2016-05-22 21:36:58 +02:00
Spidev46 49b536cab0 There is a mistake with the messages (#153)
Confirmation message of reselling a region does not go to the old owner.
2016-05-22 21:19:10 +02:00
Thijs Wiefferink 9a9969dea3 Fix problems with detecting most important region when placing a sign.
WorldGuard filters child regions from the ApplicableRegionSet for some reason, so setting parent/child relations on regions messes up the detection because the children can never be found.
2016-03-07 12:21:33 +01:00
Thijs Wiefferink 84e4b49bfd Update language files
English, Dutch and German are complete, others are only partially complete.
2016-03-06 00:07:31 +01:00
Thijs Wiefferink 6daaf6997d 1.9 compatibility fixes (getOnlinePlayers() method) 2016-03-05 22:15:21 +01:00
Thijs Wiefferink c445a62334 Add variable support to all messages where a region is known (mostly messages of commands were not using it yet). 2016-02-08 19:39:33 +01:00
Thijs Wiefferink 0d205b0d9e Change general.teleportLocationY to also accept a number. 2016-02-08 14:43:24 +01:00
Thijs Wiefferink 8c33826c2c Add build flag to default flagprofiles, users are often having trouble with this 2016-01-26 22:35:01 +01:00
Thijs Wiefferink 51e1dbafb7 Fix running sign commands going wrong.
Player commands are run first, which normally rents/buys a region, so when the console commands are ran after that, then it will run the commands of the wrong state (since the state just changed). Also fixed a minor WorldGuard version dependency issue.
2016-01-26 22:20:45 +01:00
Thijs Wiefferink 6edad3faff Add priority and interact flag to config.yml by default
50% of the issues are having more regions at the same place, and therefore requiring a higher priority for the AreaShop region and having chests accessible by anyone instead of only the region owner/member. This should fix these problems.
2016-01-12 21:43:04 +01:00
Thijs Wiefferink f89e0f861b Add a link to a preview server 2016-01-08 20:18:47 +01:00
Thijs Wiefferink 770b1d98df Refactoring:
- Move a bunch of utility methods from the AreaShop class to Utils
- Comments styling cleanup
- Display warning in console when region file does not load correctly (is empty)
2016-01-05 22:31:55 +01:00
Thijs Wiefferink 8e5baa47af Fix variables of sell messages 2015-12-26 20:08:01 +01:00
Thijs Wiefferink 367466735e Fix the 'n:<name>' option for member/owner region flags
Before it would keep the 'n:' part when adding the name to the region, therefore adding an incorrect name.
2015-12-26 00:45:05 +01:00
Thijs Wiefferink ade6bc3971 Add AddedRegionEvent+RemovedRegionEvent, moved sign/flags updating
- Sign updating now uses the event system
- WorldGuard region flag updating now uses the event system
- After changes made by commands/other user input it will now use update() on the region instead of the sign and region flag commands.
2015-12-13 19:55:47 +01:00
Thijs Wiefferink 05fd8c0156 Implement event system
- 'ask' type events that can be cancelled, broadcasted before the action happens
- 'notify' type events that cannot be cancelled, but only let you know that it has happened
- Events for renting (with extending flag), unrenting, buying, selling, reselling
- General 'RegionUpdateEvent' to listen to as display service (update signs, update regions flags, etc)

All messages in the GeneralRegion, RentRegion and BuyRegion classes can now use all region variables (in the future all other messages will also be worked through)

Closes #84
Closes #86
2015-12-11 20:54:12 +01:00
Thijs Wiefferink 5403472476 Cleanup/update config.yml and default.yml comments
- Add links to the wiki.
- Make things more consistent.
2015-12-10 23:16:19 +01:00
Thijs Wiefferink c4ee7bc7aa Add 'areashop.linksigns' to the 'areashop.*' permission group 2015-11-24 20:59:58 +01:00
Thijs Wiefferink 940f1969b6 Add link to dev builds on Jenkins 2015-11-24 20:43:44 +01:00
Thijs Wiefferink 07eb5477b8 Update Metrics 2015-11-17 21:21:27 +01:00
Thijs Wiefferink a4112092b5 Fix inclusion of the version specific classes of WorldGuard/WorldEdit in the final jar 2015-11-15 23:47:23 +01:00
Thijs Wiefferink 872d81b0a9 Cleanup code, fix some small bugs
Most of these things have been found with help of Intellij IDEA, preferring it over Eclipse already
2015-11-15 23:35:48 +01:00
Thijs Wiefferink b04871c553 Add rental to max when rental would go above maxRentTime otherwise
- Setting `extendToFullWhenAboveMaxRentTime` to set if AreaShop should
automatically extend to max when the player cannot rent a full duration
because of `maxRentTime`.

Fixes #94
2015-11-11 11:56:16 +01:00
Thijs Wiefferink fd1b8fba4c Fix inactiveSetting debug messages
The messages still assumed the inactiveSettings was in minutes instead
of in milliseconds.
2015-11-02 22:39:40 +01:00
Thijs Wiefferink 991e7b1961 Fix lastActive sometimes not updating correctly
- Update the lastActive time at rent/buy to ensure it is non-zero
- Ignore lastActive time of zero, give back current time
- Update lastActive time at server shutdown for players that are still
online, quit events are not fired at shutdown
2015-11-02 22:32:51 +01:00
Thijs Wiefferink aac963325a CI run :) 2015-11-01 23:03:41 +01:00
Thijs Wiefferink 3e7c7ce76b Fix message variables for '/as info player <player>' feedback 2015-10-31 23:15:11 +01:00
Thijs Wiefferink c858d30481 Add .gitignore to the repository 2015-10-19 19:07:31 +02:00
Thijs Wiefferink b090fd5577 Remove MCStats image, site is more offline than online 2015-10-17 22:10:38 +02:00
Thijs Wiefferink 8d132c66c4 Prepare for the v2.2.1 release
- Update version number
- Update language files from Transifex
2015-10-17 21:03:30 +02:00
Thijs Wiefferink 50e3e59543 Fix indentation and error message of WorldEdit 6 integration handler 2015-10-15 12:13:15 +02:00
Thijs Wiefferink eb21b9567e Change signtags from [arent] and [abuy] to [asrent] and [asbuy]
Changes #45
2015-10-08 22:49:41 +02:00
Thijs Wiefferink 4dba305f6d Change the default sign tags to something more specific
Note: Change documentation pages to include this.

Fixes #45
2015-10-06 22:41:59 +02:00
Thijs Wiefferink 7481d8287f Retrieve money from landlords when unrenting/selling a region.
Prevents exploits by giving money to landlords, and then
unrenting/selling to get money back from 'the server' (nobody). Now it
will get the money back from the landlord, or if that is not possible
then not give anything back.

Fixes #75
2015-10-06 22:15:09 +02:00
Thijs Wiefferink 4d41decbe5 Config part of week indicators 2015-10-03 21:14:24 +02:00
Thijs Wiefferink 7fc5a5af81 Add weeks time indicators, clean other indicators
The old Dutch time indicators have been removed from the lists, this
might cause problems for people if they regenerate their config but
don't add these tags again.

Added console warnings at startup to indicate incorrect duration
strings.

Fixes #32
2015-10-03 21:12:38 +02:00
Thijs Wiefferink d26fa99b95 Fix using the seconds modifier with /as setduration
AreaShop could correctly read and use all seconds modifiers that are
defined in the config, but the `/as setduration` command would not
accept it as input.

Fixes #82
2015-10-03 20:38:38 +02:00
Thijs Wiefferink 39d755d2db Make removing regions from AreaShop less destructible for region flags
First AreaShop would reset all flags, now it will only reset the
greeting and farewell flags. This is to make sure players do not get
confused about the region, if the greeting is still there then players
might think it is still connected to AreaShop.

Fixes #72
2015-10-03 20:27:19 +02:00
Thijs Wiefferink 48ab46ecf7 Fix indirect breaking of signs causing it to disconnect
If you would break a block behind/below a sign AreaShop would disconnect
it from the region, which is most of the times not the wanted behaviour.
This commit changes it to simply leave a floating sign there, so that it
is still visible and not disconnected from the region.

Fixes #15
2015-10-03 20:10:44 +02:00
Thijs Wiefferink b3fa31e1cc Remove debug message 2015-10-02 20:44:25 +02:00
Thijs Wiefferink 783085fbf7 Cache region variable replacements 2015-10-02 20:41:00 +02:00
Thijs Wiefferink aa093b0c8a Fix sign clicking still executing while event is cancelled. 2015-09-26 01:06:22 +02:00
Thijs Wiefferink 9c357fe929 Remove lastActive from region file when unrented/sold
No problems if it is there, but just keeping the region file clean.
2015-09-11 22:44:12 +02:00
Thijs Wiefferink 4d70571a23 Improve failed file loading
- Detects incorrectly loaded config.yml and default.yml
(YamlConfiguration prints errors to console and continues silently,
returning an empty configuration if something is wrong normally)
- Will go into error state when loading fails
- Improved some error handling to make sure that no extra errors are
thrown because of trying to close non-existing streams.
2015-09-10 21:58:57 +02:00
Thijs Wiefferink 29a4105ece Add Vault to the repo to make it easier to compile 2015-09-06 17:05:38 +02:00
Thijs Wiefferink edc7136063 Fix '/as info' always showing landlord line
It would always show the landlord line, if there was no actual landlord
it would show '<UNKNOWN>' as name, which is very confusing.

Fixes #46
2015-09-06 16:42:16 +02:00
Thijs Wiefferink 6fabc75be4 Fix '/as info' trying to filter by group too much
'/as info region' would also try to filter by group using the region
name, which would almost always cause an error message (group not found)
2015-09-06 16:38:23 +02:00
Thijs Wiefferink 085507bf8b Add required dependencies section 2015-08-30 00:10:12 +02:00
Thijs Wiefferink 73ed3366ae Update README.md 2015-08-29 22:27:14 +02:00
Thijs Wiefferink 9ca029176d Fix '/as find' not getting your balance correctly
- If you search with your balance (and do not give a maxprice) it would
always use 0.0 as your balance, therefore not finding any regions.
- Also fixes the incorrect feedback, it would say it searched with a
maxprice instead of your balance and the other way around if no regions
are found.
- Fixed maxprice and balance display to use correct currency symbols and
layouts (decimals, metric prefixes, separator).

Fixes #59
2015-08-29 20:06:16 +02:00
Thijs Wiefferink bf181d69d7 Update README.md 2015-08-29 19:46:05 +02:00
Thijs Wiefferink 68c57515da Update README.md 2015-08-29 19:44:54 +02:00
Thijs Wiefferink 903d6c309f Update README.md 2015-08-29 19:44:39 +02:00
Thijs Wiefferink 0a02eb3254 Update README.md 2015-08-29 19:42:57 +02:00
Thijs Wiefferink bf04589fab Merge links to issues with the top menu 2015-08-29 19:42:11 +02:00
Thijs Wiefferink 14dd094803 Update README.md 2015-08-27 22:51:49 +02:00
Thijs Wiefferink 02df35304c Update README.md 2015-08-27 22:48:11 +02:00
Thijs Wiefferink 4321667ec5 Merge branch 'master' of https://github.com/NLthijs48/AreaShop 2015-08-26 20:05:03 +02:00
Thijs Wiefferink 5cf5df78c8 Add permissions to prevent buying resell regions
- Permission 'areashop.buynormal' can be negated to only allow buying
regions that are in resell mode
- Permission 'areashop.buyresell' can be negated to only allow buying
regions that are not in resell mode

This change does not affect existing permissions setups (above
permissions are true by default)
2015-08-26 20:04:50 +02:00
Thijs Wiefferink 8608d6ce1d Update README.md 2015-08-25 17:40:08 +02:00
Thijs Wiefferink 614336862e Update README.md 2015-08-25 17:38:02 +02:00
Thijs Wiefferink a4ea175f1b Update README.md 2015-08-25 17:33:52 +02:00
Thijs Wiefferink c96043aa38 Update README.md 2015-08-25 17:30:11 +02:00
Thijs Wiefferink d5a29f6c32 Update README.MD: WIP frontpage
Frontpage like the BukkitDev and Spigot Resources section.
2015-08-25 17:29:21 +02:00
Thijs Wiefferink 5176a1c2c5 Update README.md 2015-08-21 21:45:42 +02:00
Thijs Wiefferink 89569ea182 Update README.md 2015-08-17 23:11:46 +02:00
Thijs Wiefferink f349657a59 Fix bug with teleporting to signs
Fixes #47
2015-08-13 22:04:22 +02:00
Thijs Wiefferink 40d3d4d8af Add group filtering to /as info <rented|forrent|sold|forsale>
- Filter regions by group for the mentioned subcommands of /as info
- Clean/refactor code
- Sort the output
- Add Comparable, equals() and toString() to GeneralRegion

Closes #44
2015-08-11 22:41:30 +02:00
Thijs Wiefferink 0d5db8fa4e Add option to deny extending when over rent/total limits
Turned on by default, added config setting
'allowRegionExtendsWhenAboveLimits' to disable this change. Normally
this is expected behaviour, which pushes users to let the region run out
if they should not be able to rent it anymore (was impossible to do
before).

Closes #41
2015-08-10 21:43:29 +02:00
Thijs Wiefferink 25c56e8ecb Add permissions for landlords to remove regions
Closes #33
2015-07-31 22:56:12 +02:00
Thijs Wiefferink 9f436a7eb4 Fix landlord UUID/name handling
Also changes handling of buyer/renter UUIDs and names, if the playerdata
of a player is not available anymore AreaShop will still be able to
display his name. Also unrenting/selling will still properly pay back
the player through Vault (by name instead of UUID).

Fixes #35
2015-07-30 22:55:49 +02:00
Thijs Wiefferink f76c1b8a9f Save last online time self instead of using the Bukkit API
The Bukkit API returns an incorrect last online time when the player.dat
file is not present (for example when a server saves inventories etc.
somewhere else, or when it is cleaned).

Improves landlord handling when there is no UUID present in the config
files.

Closes #34
2015-07-28 23:02:52 +02:00
Thijs Wiefferink d466658f36 Allow only providing landlord name, instead of UUID 2015-07-28 21:50:28 +02:00
Thijs Wiefferink 04e02c4ce4 Adds a way to change number length for stack region names
Adds dots after all config explaination lines, fixes a couple things
with groups.yml file saving (would save unnecessary sometimes).

Fixes #26
2015-07-26 20:56:44 +02:00
Thijs Wiefferink 3bf493109c Add date/time variables for commands/schematics
Fixes #21
2015-07-26 20:15:45 +02:00
Thijs Wiefferink 2f9b6d3987 Update language files to the latest available versions on Transifex 2015-07-25 16:57:24 +02:00
Thijs Wiefferink 93d5612513 Fix a bug with depositing to players that never joined
Paying to players that never visited before will now fallback to using a
name instead of UUID towards Vault, which prevents problems (for players
that never visited, such as server accounts, you cannot get the name
from the UUID via Bukkit).
2015-07-25 16:56:41 +02:00
Thijs Wiefferink 937db4620d Fix problem with default value 'unlimited' for maxRentTime 2015-06-21 19:29:54 +02:00
Thijs Wiefferink a0239b8837 Fix loading when there is no regions folder 2015-06-21 17:08:11 +02:00
Thijs Wiefferink 401f52e687 Change additional places where the contains is used 2015-06-13 22:56:27 +02:00
Thijs Wiefferink 2f6874a16e Fix contains check for WorldGuard regions not version dependend 2015-06-13 22:50:06 +02:00
Thijs Wiefferink c95f4e1c6c Fix problem with '/as addfriend' denying first-joined players
Before it checked if the player had played before on the server in order
to check if the friend will get accepted, but this fails when the player
is playing for the first time. This has been repaired.
2015-06-13 22:06:34 +02:00
Thijs Wiefferink 1a2d755727 Fix message using incorrect time unit 2015-06-11 15:42:39 +02:00
Thijs Wiefferink c489e6eba4 Commit changes in language file 2015-06-11 15:32:29 +02:00
Thijs Wiefferink b1a56b5230 Fix maxRentTime, inactiveTimeUntilUnrent/-Sell settings
Made a misake while converting these settings to accept human-readable
durations strings instead of only minutes.
2015-06-11 14:11:41 +02:00
Thijs Wiefferink dfbc7805b5 Fix a couple of bugs 2015-06-11 00:51:44 +02:00
Thijs Wiefferink 8f2c95f5c4 Merge branch 'master' of https://github.com/NLthijs48/AreaShop 2015-05-22 19:01:40 +02:00
Thijs Wiefferink 0b9bffe68f Fix bug with changed time settings not loading old values 2015-05-22 19:01:30 +02:00
Thijs Wiefferink 7ac6e34c89 Update README.md 2015-05-12 10:25:07 +02:00
Thijs Wiefferink 5231f6e86e Change all time period settings to readable format
Changed all settings that took minutes or seconds as unit before to use
the format also used on signs (things like '50 seconds', '1 day', '3
months' etc).
Fixed a bug with display of currency numbers being unrounded when not
displayed with SI prefix.
2015-05-03 22:51:19 +02:00
Thijs Wiefferink 791b696aff Add support for Metric prefixes to display large numbers
Indications are used as defined on:
http://en.wikipedia.org/wiki/Metric_prefix, implemented from 'k' to 'Y'.
Added setting 'metricSuffixesAbove' to config.yml to specify from which
number these characters should be used.
Added setting 'decimalMark' to change the character(s) used as decimal
separation (is a comma instead of a dot in certain countries).
2015-05-03 00:15:05 +02:00
Thijs Wiefferink c92bc716dc Change module names to fit project setup
Forgot to rename the folders that contain the interface classes. Cleaned
pom.
2015-05-02 00:33:34 +02:00
Thijs Wiefferink 463b34e952 Merge pull request #13 from Androkai/patch-1
fix typo "targer" --> "target"
2015-05-01 11:02:53 +02:00
Androkai ff6e69196d fix typo "targer" --> "target" 2015-04-30 22:50:43 +02:00
Thijs Wiefferink 76ad952bb6 Remove unnecessary marking as 'to be saved'
Marking as 'to be saved' is already done when a setting change is
written to the YAML file, so it does not have to be called from the
places where settings are changed.
2015-04-28 20:38:44 +02:00
Thijs Wiefferink 0cdff5e143 Move README.md to the correct place. 2015-04-28 18:56:13 +02:00
Thijs Wiefferink 9dc3013215 Update README.md 2015-04-28 18:54:30 +02:00
Thijs Wiefferink 796b95c9f9 Change to a Maven project
Changed project layout to Maven, all dependencies except Vault (does not
have a maven repo) are now included in the pom.xml and will be
automatically downloaded by Maven when building. The project setup uses
modules to support both WorldGuard and WorldEdit version with 1 jar file
instead of having 2 files as the last versions of AreaShop had.

Small bug with boolean settings in the config files has been fixed.
Problem with '/as stack' stacking in the Y direction instead of Z has
been fixed. Plus some refactoring and small changes.
2015-04-28 18:53:42 +02:00
Thijs Wiefferink 2785a72457 Update README.md 2015-04-10 18:50:42 +02:00
Thijs Wiefferink c2df0cd912 Package and classes explanation 2015-04-10 18:47:20 +02:00
Thijs Wiefferink 4cd32a0792 Update README.md 2015-04-10 17:57:45 +02:00
Thijs Wiefferink 354e70af57 Add basic instructions for compiling AreaShop
Intructions to compile AreaShop from the source files provided here at GitHub.
2015-04-10 17:57:00 +02:00
Thijs Wiefferink fde60e2a27 Add permissions for landlords to add regions
Added things:
- "areashop.createrent.owner", "areashop.createrent.member",
"areashop.createbuy.owner" and "areashop.createbuy.member" to let
players add regions to AreaShop when they are member/owner of the
WorldGuard region (because they made the region for example).
- "areashop.setprice.landlord" to give landlords access to setting the
price for their regions
- "areashop.setduration.landlord" to give landlords access to setting
the duration for their regions
- When "default" or "reset" is given as price then the price will be
removed and therefore inherited from a group or default.yml
- When "default" or "reset" is given as duration then the duration will
be removed and therefore inherited from a group or default.yml
2015-03-17 21:22:57 +01:00
Thijs Wiefferink d1a3027e0b Update language files to the current status
Polish is back and 88% translated, Finnish is new and fully translated.
2015-03-13 21:16:44 +01:00
Thijs Wiefferink 4e57f03fc0 Added a command to set the landlord of a region.
The landlord receives all money from rents/buys of the region. Reselling
with '/as resell' does not transfer money to the landlord.
Unrenting/selling will not substract money from the account of the
landlord, so set the 'moneyBack' to 0 to prevent cheating. The
'landlord' setting is also present in the general section of
default.yml, so this means you can also add this setting to a region
group to easily make a group of regions give money to 1 player.
2015-03-13 21:07:59 +01:00
Thijs Wiefferink 83d4efc3a8 Remove redundant setting from the config to enable schematics
The 'enableSchematics' boolean setting from the config.yml file is not
usefull, 'enableRestore' in default.yml already handles the same thing,
and is more flexible (per region or region group instead of general)
2015-03-13 18:25:24 +01:00
Thijs Wiefferink 1395afffe9 Fix a bug with teleporting
Added messages when teleporting automatically changes to teleporting to
a sign instead of to the region or other way around.
2015-03-13 14:41:54 +01:00
Thijs Wiefferink 5094d39a96 Get worlds for WorldEdit the proper way (needs wrapped world) 2015-02-28 19:40:43 +01:00
Thijs Wiefferink e664c1efd9 Load regions at startup, check them at the first tick 2015-02-24 11:48:58 +01:00
Thijs Wiefferink 871eeed29c Change the loading of regions to all at once again
Otherwise it would cause problems with plugins that hook into AreaShop
and try to get regions.
2015-02-23 22:32:54 +01:00
Thijs Wiefferink 0d56762bfc Change to Vault integration to allow custom economies
AreaShop would not use an economy if it would be loaded after it.
2015-02-23 22:21:52 +01:00
Thijs Wiefferink c08763883f Fix a bug with '/as setowner' not saving the region file
Changed the way position the regions are marked as 'to be saved' to
prevent this problem in the future.
2015-02-23 21:48:57 +01:00
Thijs Wiefferink efb534f131 Fix the schematic saving 2015-02-23 19:26:20 +01:00
Thijs Wiefferink d9405a9f49 Make the updatechecker check async
If the updatechecker cannot reach BukkitDev it would slow down the
startup of the server up to 30 seconds (and also if you use /reload),
this will not happen anymore.
2015-02-23 18:11:51 +01:00
Thijs Wiefferink 684add89cd Change the way regions are loaded
Region loading is now started at the first server tick, and will do a
certain number of regions per tick ('loading.regionsPerTick' config
setting). This prevents problems with plugins that load worlds after
AreaShop is loaded. If AreaShop detects that a region does not have a
correct WorldGuard region assigned anymore or the world of the region is
not loaded it will be printed to the console, but it will not delete the
region anymore (which would lead to data loss).
2015-02-23 17:58:10 +01:00
Thijs Wiefferink d1848c0728 Delete .project 2015-02-23 13:32:04 +01:00
Thijs Wiefferink 9fd16290c0 Fix some unclear and wrong messages in the language file
Thanks to qlick for suggesting these language changes. Also fixes a bug
with the '/as info player' command, would cause an error if a region has
an incorrect UUID as owner or an UUID without name.
2015-02-22 23:16:37 +01:00
Thijs Wiefferink 3bbca11cc9 Added the '/as stack' command to easily create and register regions
/as stack <amount> <gap> <name> <rent|buy> [group]
    <amount> Number of regions that will be created
    <gap> Blocks distance between the regions
    <name> Name of the regions (number will be behind it)
    <rent|buy> The type of the region
    [group] A group to add the created regions to
    The regions will be created in the direction you are facing
2015-02-22 17:08:32 +01:00
Thijs Wiefferink b1be87014f Change schematic functions to new WorldGuard 6 classes 2015-02-19 17:22:06 +01:00
242 changed files with 27584 additions and 14339 deletions

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
---
name: Bug report
about: Report a problem in the plugin
title: ''
labels: 'NEW, Type: Bug'
assignees: ''
---
**Current behavior**
<!--- Tell what happens currently and/or show the stacktrace if an error occured -->
**Expected behavior**
<!--- Tell what you expected to happen instead -->
**Steps to reproduce**
<!--- Exactly show how this bug can be triggered -->
1.
2.
3.
4.
**Your environment**
<!--- Indicate which versions you use -->
<!--- Attention: "LATEST" or "NEWEST" are invalid answers! -->
<!--- Use '/version' for the Platform version and '/version <plugin>' for plugin versions -->
* Server platform (Bukkit, Spigot, etc.):
* Server version:
* AreaShop version:
* WorldEdit version:
* WorldGuard version:
* Vault version:
**Your config files**
<!--- Attach a '.zip' file of the '/plugins/AreaShop' and '/plugins/WorldGuard' directories to show your configuration setup -->
+15
View File
@@ -0,0 +1,15 @@
---
name: Feature request
about: Suggest an idea
title: ''
labels: 'NEW, Type: Feature'
assignees: ''
---
**Describe the problem you are trying to solve with this feature request**
<!-- When I know the problem you are trying to solve I can help you to think about the best solution -->
**Describe the solution you'd like**
<!-- Describe the config settings, commands and other features that should be added -->
@@ -0,0 +1,31 @@
---
name: Support question
about: Ask help with the setup or usage of the plugin
title: ''
labels: 'NEW, Type: Support'
assignees: ''
---
**Current behavior**
<!--- Tell what happens currently -->
**Desired behavior**
<!--- Tell what you want to happen instead -->
**Your environment**
<!--- Indicate which versions you use -->
<!--- Attention: "LATEST" or "NEWEST" are invalid answers! -->
<!--- Use '/version' for the Platform version and '/version <plugin>' for plugin versions -->
* Server platform (Bukkit, Spigot, etc.):
* Server version:
* AreaShop version:
* WorldEdit version:
* WorldGuard version:
* Vault version:
**Your config files**
<!--- Attach a '.zip' file of the '/plugins/AreaShop' and '/plugins/WorldGuard' directories to show your configuration setup -->
+23
View File
@@ -0,0 +1,23 @@
name: Java CI with Gradle
on: [ push, pull_request ]
jobs:
build:
# Only run on PRs if the source branch is on someone else's repo
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest"]
runs-on: "${{ matrix.os }}"
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1.0.4
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'zulu'
cache: 'gradle'
- name: Build with Gradle
run: ./gradlew clean build
+194
View File
@@ -0,0 +1,194 @@
# Created by https://www.toptal.com/developers/gitignore/api/macos,windows,intellij+all,gradle,maven
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,windows,intellij+all,gradle,maven
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/*
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
# Eclipse m2e generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
### Gradle ###
.gradle
build/
build-cache/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
### Gradle Patch ###
**/build/
# End of https://www.toptal.com/developers/gitignore/api/macos,windows,intellij+all,gradle,maven
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>NLThijs48 - AreaShop</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
+114
View File
@@ -0,0 +1,114 @@
plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
}
description = "AreaShop"
dependencies {
// Platform
compileOnlyApi(libs.spigot)
compileOnlyApi(libs.worldeditCore) {
exclude("com.google.guava", "guava")
}
compileOnlyApi(libs.worldeditBukkit) {
exclude("com.google.guava", "guava")
}
compileOnlyApi(libs.worldguardCore) {
exclude("com.google.guava", "guava")
}
compileOnlyApi(libs.worldguardBukkit) {
exclude("com.google.guava", "guava")
}
compileOnlyApi("com.github.MilkBowl:VaultAPI:1.7") {
exclude("com.google.guava", "guava")
}
// 3rd party libraries
implementation("io.papermc:paperlib:1.0.8")
implementation("com.github.NLthijs48:InteractiveMessenger:e7749258ca")
implementation("com.github.NLthijs48:BukkitDo:819d51ec2b")
implementation("io.github.baked-libs:dough-data:1.2.0")
implementation("com.google.inject:guice:7.0.0") {
exclude("com.google.guava", "guava")
}
implementation("com.google.inject.extensions:guice-assistedinject:7.0.0") {
exclude("com.google.guava", "guava")
}
implementation("net.kyori:adventure-platform-bukkit:4.3.0")
implementation("net.kyori:adventure-text-minimessage:4.14.0")
implementation("org.spongepowered:configurate-yaml:4.0.0")
// Project submodules
implementation(projects.areashopInterface)
implementation(projects.areashopNms)
implementation(projects.adapters.platform.platformInterface)
implementation(projects.adapters.platform.paper)
runtimeOnly(projects.adapters.plugins.worldedit)
runtimeOnly(projects.adapters.plugins.worldguard)
runtimeOnly(projects.adapters.plugins.fastasyncworldedit)
// Adapters
if (!providers.environmentVariable("JITPACK").isPresent) {
runtimeOnly(project(":adapters:platform:bukkit-1-17", "reobf"))
runtimeOnly(project(":adapters:platform:bukkit-1-18", "reobf"))
runtimeOnly(project(":adapters:platform:bukkit-1-19", "reobf"))
runtimeOnly(project(":adapters:platform:bukkit-1-20", "reobf"))
}
testImplementation("com.github.seeseemelk:MockBukkit-v1.20:3.9.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}
repositories {
mavenCentral()
}
tasks {
processResources {
filesMatching("plugin.yml") {
expand("version" to project.version)
}
}
assemble {
if (!providers.systemProperty("JITPACK").isPresent) {
dependsOn(shadowJar)
}
}
jar {
archiveClassifier.set("original")
}
java {
withSourcesJar()
}
test {
useJUnitPlatform()
}
val javaComponent = project.components["java"] as AdhocComponentWithVariants
javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) {
skip()
}
shadowJar {
archiveClassifier.set("")
base {
archiveBaseName.set("AreaShop")
}
val base = "me.wiefferink.areashop.libraries"
relocate("me.wiefferink.interactivemessenger", "${base}.interactivemessenger")
relocate("me.wiefferink.bukkitdo", "${base}.bukkitdo")
relocate("io.papermc.lib", "${base}.paperlib")
relocate("io.github.bakedlibs.dough", "${base}.dough")
relocate("com.google.inject", "${base}.inject")
relocate("org.aopalliance", "${base}.aopalliance")
relocate("javax.annotation", "${base}.javax.annotation")
relocate("javax.inject", "${base}.javax.inject")
}
}
@@ -0,0 +1,145 @@
package io.github.md5sha256.areashop;
import me.wiefferink.areashop.managers.AdventureLanguageManager;
import me.wiefferink.interactivemessenger.processing.ReplacementProvider;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import java.text.NumberFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AdventureMessageRenderer {
/**
* Lang pattern used by the old mechanism
* Equivalent REGEX: \Q%\E\Qlang:\E([a-zA-Z-]+?)\|(.+?)\|\Q%\E
* Group 1 is the message key for substitution
* Group 2 is the replacement arguments
* NOTE: A properly formatted key should have a trailing '|' char.
*/
private static final Pattern LANG_PATTERN = Pattern.compile(
Pattern.quote("%") + "lang:([a-zA-Z-]+?)\\|(.+)\\|+" + Pattern.quote("%")
);
private static final Pattern POSITIONAL_ARGUMENT_PATTERN = Pattern.compile(
Pattern.quote("%") + "(\\d+)" + Pattern.quote("%")
);
private static final Pattern NAMED_ARGUMENT_PATTERN = Pattern.compile(Pattern.quote(
"%") + "(?<name>[a-zA-Z]+)" + Pattern.quote("%"));
private static final Pattern VARIABLE_DELIMITER = Pattern.compile("\\|");
private final AdventureLanguageManager adventureLanguageManager;
public AdventureMessageRenderer(AdventureLanguageManager languageManager) {
this.adventureLanguageManager = languageManager;
}
public String chatPrefix() {
return this.adventureLanguageManager.chatPrefix();
}
public Component renderedChatPrefix() {
return renderMessage(this.adventureLanguageManager.chatPrefix());
}
public Component renderMessageFromKey(String key, Object... replacements) {
String message = this.adventureLanguageManager.messageFor(key);
if (message.isEmpty()) {
return Component.empty();
}
return renderMessage(message, replacements);
}
public String performReplacementsFromKey(String key, Object... replacements) {
String message = this.adventureLanguageManager.messageFor(key);
if (message.isEmpty()) {
return message;
}
return performReplacements(message, replacements);
}
public String performReplacements(String message, Object... replacements) {
String result = processPositionalArgumentReplacement(message, replacements);
return processLanguageReplacements(result, replacements);
}
public Component renderMessage(String message, Object... replacements) {
MiniMessage miniMessage = MiniMessage
.builder()
.preProcessor(string -> performReplacements(string, replacements))
.build();
return miniMessage.deserialize(message);
}
private void processNamedArguments(String[] arguments, Object... replacements) {
for (int i = 0; i < arguments.length; i++) {
if (i >= replacements.length) {
continue;
}
String argument = arguments[i];
Matcher namedMatcher = NAMED_ARGUMENT_PATTERN.matcher(argument);
if (!namedMatcher.matches()) {
continue;
}
Object rawReplacement = replacements[i];
if (!(rawReplacement instanceof ReplacementProvider provider)) {
continue;
}
String replacement = parseString(provider.provideReplacement(argument));
arguments[i] = replacement;
}
}
private String processLanguageReplacements(String text, Object... replacements) {
Matcher langMatcher = LANG_PATTERN.matcher(text);
return langMatcher.replaceAll(result -> {
String langKey = result.group(1);
String rawArguments = result.group(2);
String[] arguments = VARIABLE_DELIMITER.split(rawArguments);
processNamedArguments(arguments, replacements);
String innerMessage = this.adventureLanguageManager.messageFor(langKey);
return processInnerPositionalArgumentReplacements(innerMessage, arguments);
});
}
private String processInnerPositionalArgumentReplacements(String text, String[] replacements) {
final Matcher matcher = POSITIONAL_ARGUMENT_PATTERN.matcher(text);
return matcher.replaceAll(result -> {
int position = Integer.parseInt(result.group(1));
if (position < 0 || position > replacements.length - 1) {
return "";
}
return replacements[position];
});
}
private String processPositionalArgumentReplacement(String text, Object... replacements) {
final Matcher matcher = POSITIONAL_ARGUMENT_PATTERN.matcher(text);
return matcher.replaceAll(result -> {
int position = Integer.parseInt(result.group(1));
if (position < 0 || position > replacements.length - 1) {
return "";
}
Object replacement = replacements[position];
if (!(replacement instanceof ReplacementProvider)) {
return replacement.toString();
}
return "";
});
}
private String parseString(Object replacementResult) {
if (replacementResult instanceof String s) {
return s;
}
if (replacementResult instanceof Component component) {
return MiniMessage.miniMessage().serialize(component);
}
if (replacementResult instanceof Number number) {
return NumberFormat.getNumberInstance().format(number);
}
return replacementResult.toString();
}
}
@@ -0,0 +1,51 @@
package io.github.md5sha256.areashop;
import me.wiefferink.interactivemessenger.processing.Message;
import java.util.ArrayList;
import java.util.List;
public class CompositeMessage implements MessageWrapper {
private final List<MessageWrapper> children = new ArrayList<>();
@Override
public Message toLegacyMessage() {
Message message = Message.empty();
for (MessageWrapper wrapper : children) {
message.append(wrapper.toLegacyMessage());
}
return message;
}
@Override
public String toMiniMessage(AdventureMessageRenderer messageRenderer) {
StringBuilder message = new StringBuilder();
for (MessageWrapper wrapper : children) {
message.append(wrapper.toMiniMessage(messageRenderer));
}
return message.toString();
}
public CompositeMessage appendMessage(MessageWrapper wrapper) {
children.add(wrapper);
return this;
}
public CompositeMessage appendText(String text) {
this.children.add(new TextMessage(text));
return this;
}
public CompositeMessage appendMessage(String key, boolean prefix, Object... replacements) {
this.children.add(new KeyedMessage(key, prefix, replacements));
return this;
}
public CompositeMessage appendMessage(String key, Object... replacements) {
this.children.add(new KeyedMessage(key, replacements));
return this;
}
}
@@ -0,0 +1,20 @@
package io.github.md5sha256.areashop;
import me.wiefferink.interactivemessenger.processing.Message;
public record KeyedMessage(String messageKey, boolean prefix, Object... replacements) implements MessageWrapper{
public KeyedMessage(String messageKey, Object... replacements) {
this(messageKey, false, replacements);
}
@Override
public Message toLegacyMessage() {
return Message.fromKey(messageKey).prefix(prefix).replacements(replacements);
}
@Override
public String toMiniMessage(AdventureMessageRenderer messageRenderer) {
String chatPrefix = messageRenderer.chatPrefix();
return chatPrefix + messageRenderer.renderMessageFromKey(messageKey);
}
}
@@ -0,0 +1,89 @@
package io.github.md5sha256.areashop;
import me.wiefferink.interactivemessenger.generators.TellrawGenerator;
import me.wiefferink.interactivemessenger.parsers.YamlParser;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.loader.HeaderMode;
import org.spongepowered.configurate.yaml.NodeStyle;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class LanguageConverter {
public static void performConversion(ConfigurationNode root) throws ConfigurateException {
if (!root.isMap()) {
convertNode(root);
return;
}
Map<Object, ? extends ConfigurationNode> map = root.childrenMap();
for (Map.Entry<Object, ? extends ConfigurationNode> entry : map.entrySet()) {
ConfigurationNode node = entry.getValue();
if (node.isMap()) {
performConversion(node);
} else {
convertNode(node);
}
}
}
public static void performConversion(File toConvert, File output) throws IOException {
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
.source(() -> new BufferedReader(new FileReader(toConvert, StandardCharsets.UTF_8)))
.sink(() -> new BufferedWriter(new FileWriter(output, StandardCharsets.UTF_8)))
.headerMode(HeaderMode.PRESERVE)
.nodeStyle(NodeStyle.BLOCK)
.build();
ConfigurationNode root = loader.load();
performConversion(root);
loader.save(root);
}
public static List<String> convertRawList(List<String> messages) {
List<String> jsonMessages = TellrawGenerator.generate(YamlParser.parse(messages));
List<String> converted = new ArrayList<>();
for (String json : jsonMessages) {
BaseComponent[] bungeeComponents = ComponentSerializer.parse(json);
Component convertedComponent = BungeeComponentSerializer.get()
.deserialize(bungeeComponents);
String miniMessage = MiniMessage.miniMessage()
.serialize(convertedComponent);
converted.add(miniMessage);
}
return converted;
}
private static void convertNode(ConfigurationNode node) throws ConfigurateException {
boolean isList = node.isList();
List<String> messages;
if (isList) {
messages = node.getList(String.class, Collections.emptyList());
} else {
messages = Collections.singletonList(node.getString());
}
List<String> converted = convertRawList(messages);
if (converted.size() == 1) {
node.set(String.class, converted.get(0));
} else {
node.setList(String.class, converted);
}
}
}
@@ -0,0 +1,11 @@
package io.github.md5sha256.areashop;
import me.wiefferink.interactivemessenger.processing.Message;
public interface MessageWrapper {
Message toLegacyMessage();
String toMiniMessage(AdventureMessageRenderer messageRenderer);
}
@@ -0,0 +1,22 @@
package io.github.md5sha256.areashop;
import me.wiefferink.interactivemessenger.processing.Message;
public class TextMessage implements MessageWrapper {
private final String text;
public TextMessage(String text) {
this.text = text;
}
@Override
public Message toLegacyMessage() {
return Message.empty().append(text);
}
@Override
public String toMiniMessage(AdventureMessageRenderer messageRenderer) {
return messageRenderer.performReplacements(text);
}
}
@@ -0,0 +1,914 @@
package me.wiefferink.areashop;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Stage;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.managers.RegionManager;
import io.github.md5sha256.areashop.AdventureMessageRenderer;
import io.github.md5sha256.areashop.LanguageConverter;
import io.papermc.lib.PaperLib;
import me.wiefferink.areashop.adapters.platform.MinecraftPlatform;
import me.wiefferink.areashop.adapters.platform.paper.PaperPlatform;
import me.wiefferink.areashop.features.signs.SignManager;
import me.wiefferink.areashop.interfaces.AreaShopInterface;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.listeners.PlayerLoginLogoutListener;
import me.wiefferink.areashop.managers.AdventureLanguageManager;
import me.wiefferink.areashop.managers.CommandManager;
import me.wiefferink.areashop.managers.FeatureManager;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.managers.FileManager;
import me.wiefferink.areashop.managers.Manager;
import me.wiefferink.areashop.managers.SignErrorLogger;
import me.wiefferink.areashop.managers.SignLinkerManager;
import me.wiefferink.areashop.modules.AreaShopModule;
import me.wiefferink.areashop.modules.BukkitModule;
import me.wiefferink.areashop.modules.DependencyModule;
import me.wiefferink.areashop.modules.PlatformModule;
import me.wiefferink.areashop.nms.NMS;
import me.wiefferink.areashop.tools.AdventureMessageBridge;
import me.wiefferink.areashop.tools.ForwardingMessageBridge;
import me.wiefferink.areashop.tools.GithubUpdateCheck;
import me.wiefferink.areashop.tools.LegacyMessageBridge;
import me.wiefferink.areashop.tools.SpigotPlatform;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.bukkitdo.Do;
import me.wiefferink.interactivemessenger.source.LanguageManager;
import net.milkbowl.vault.economy.Economy;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.ConfigurationVisitor;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
/**
* Main class for the AreaShop plugin.
* Contains methods to get parts of the plugins functionality and definitions for constants.
*/
public final class AreaShop extends JavaPlugin implements AreaShopApi {
// Statically available instance
private static AreaShop instance = null;
// General variables
private Injector injector;
private WorldGuardPlugin worldGuard = null;
private WorldGuardInterface worldGuardInterface = null;
private WorldEditPlugin worldEdit = null;
private WorldEditInterface worldEditInterface = null;
private NMS nms;
private MessageBridge messageBridge;
private IFileManager fileManager = null;
private LanguageManager languageManager = null;
private CommandManager commandManager = null;
private SignLinkerManager signLinkerManager = null;
private FeatureManager featureManager = null;
private SignManager signManager;
private SignErrorLogger signErrorLogger;
private Set<Manager> managers = null;
private boolean debug = false;
private List<String> chatprefix = null;
private boolean ready = false;
private GithubUpdateCheck githubUpdateCheck = null;
// Folders and file names
public static final String languageFolder = "lang";
public static final String schematicFolder = "schem";
public static final String regionsFolder = "regions";
public static final String groupsFile = "groups.yml";
public static final String defaultFile = "default.yml";
public static final String configFile = "config.yml";
public static final String configFileHidden = "hiddenConfig.yml";
public static final String signLogFile = "sign-errors.log";
public static final String versionFile = "versions";
// Euro tag for in the config
public static final String currencyEuro = "%euro%";
// Constants for handling file versions
public static final String versionFiles = "files";
public static final int versionFilesCurrent = 3;
// Keys for replacing parts of flags, commands, strings
public static final String tagPlayerName = "player";
public static final String tagPlayerUUID = "uuid";
public static final String tagWorldName = "world";
public static final String tagRegionName = "region";
public static final String tagRegionType = "type";
public static final String tagPrice = "price";
public static final String tagRawPrice = "rawprice";
public static final String tagDuration = "duration";
public static final String tagRentedUntil = "until";
public static final String tagRentedUntilShort = "untilshort";
public static final String tagWidth = "width"; // x-axis
public static final String tagHeight = "height"; // y-axis
public static final String tagDepth = "depth"; // z-axis
public static final String tagVolume = "volume"; // Number of blocks in the region (accounting for polygon regions)
public static final String tagTimeLeft = "timeleft";
public static final String tagClicker = "clicker";
public static final String tagResellPrice = "resellprice";
public static final String tagRawResellPrice = "rawresellprice";
public static final String tagFriends = "friends";
public static final String tagFriendsUUID = "friendsuuid";
public static final String tagMoneyBackPercentage = "moneybackpercent";
public static final String tagMoneyBackAmount = "moneyback";
public static final String tagRawMoneyBackAmount = "rawmoneyback";
public static final String tagTimesExtended = "timesExtended";
public static final String tagMaxExtends = "maxextends";
public static final String tagExtendsLeft = "extendsleft";
public static final String tagMaxRentTime = "maxrenttime";
public static final String tagMaxInactiveTime = "inactivetime";
public static final String tagLandlord = "landlord";
public static final String tagLandlordUUID = "landlorduuid";
public static final String tagDateTime = "datetime";
public static final String tagDateTimeShort = "datetimeshort";
public static final String tagYear = "year";
public static final String tagMonth = "month";
public static final String tagDay = "day";
public static final String tagHour = "hour";
public static final String tagMinute = "minute";
public static final String tagSecond = "second";
public static final String tagMillisecond = "millisecond";
public static final String tagEpoch = "epoch";
public static final String tagTeleportX = "tpx";
public static final String tagTeleportY = "tpy";
public static final String tagTeleportZ = "tpz";
public static final String tagTeleportBlockX = "tpblockx";
public static final String tagTeleportBlockY = "tpblocky";
public static final String tagTeleportBlockZ = "tpblockz";
public static final String tagTeleportPitch = "tppitch";
public static final String tagTeleportYaw = "tpyaw";
public static final String tagTeleportPitchRound = "tppitchround";
public static final String tagTeleportYawRound = "tpyawround";
public static final String tagTeleportWorld = "tpworld";
public static AreaShop getInstance() {
return AreaShop.instance;
}
/**
* Called on start or reload of the server.
*/
@Override
public void onEnable() {
AreaShop.instance = this;
Do.init(this);
managers = new HashSet<>();
messageBridge = new LegacyMessageBridge();
signErrorLogger = new SignErrorLogger(new File(getDataFolder(), signLogFile));
// Setup NMS Impl
String rawServerVersion = Bukkit.getServer().getClass().getPackageName();
String[] split = rawServerVersion.split("\\.");
String serverVersion = split[3];
try {
Class<?> nmsImpl = Class.forName("me.wiefferink.areashop.adapters.platform." + serverVersion + ".NMSImpl");
this.nms = nmsImpl.asSubclass(NMS.class).getConstructor().newInstance();
} catch (ReflectiveOperationException ex) {
ex.printStackTrace();
error("Failed to initialize NMS implementation!");
shutdownOnError();
return;
}
final MinecraftPlatform platform;
if (PaperLib.isPaper()) {
platform = new PaperPlatform(this);
info("Detected Paper; using the PaperPlatform impl");
} else {
platform = new SpigotPlatform(this);
info("Detected Spigot; using the SpigotPlatform impl");
}
final PlatformModule platformModule = new PlatformModule(platform, this.nms);
// Check if Vault is present
if(getServer().getPluginManager().getPlugin("Vault") == null) {
error("Vault plugin is not present or has not loaded correctly");
shutdownOnError();
return;
}
// Find WorldEdit integration version to load
String weHandlerVersion;
Plugin plugin = getServer().getPluginManager().getPlugin("WorldEdit");
if(!(plugin instanceof WorldEditPlugin) || !plugin.isEnabled()) {
error("WorldEdit plugin is not present or has not loaded correctly");
return;
} else {
this.worldEdit = (WorldEditPlugin) plugin;
Plugin fawePlugin = getServer().getPluginManager().getPlugin("FastAsyncWorldEdit");
if (fawePlugin != null && fawePlugin.isEnabled()) {
weHandlerVersion = "FastAsyncWorldEditHandler";
info("Detected FastAsyncWorldEdit, using the fawe handler");
} else {
weHandlerVersion = "WorldEditHandler";
}
}
plugin = getServer().getPluginManager().getPlugin("WorldGuard");
if(!(plugin instanceof WorldGuardPlugin) || !plugin.isEnabled()) {
error("WorldGuard plugin is not present or has not loaded correctly");
shutdownOnError();
return;
} else {
this.worldGuard = (WorldGuardPlugin) plugin;
}
DependencyModule dependencyModule = new DependencyModule(this.worldEdit, this.worldGuard, getEconomy(), getPermissionProvider());
// Load WorldEdit
try {
Class<?> clazz = Class.forName("me.wiefferink.areashop.adapters.plugins." + weHandlerVersion);
if(WorldEditInterface.class.isAssignableFrom(clazz)) { // Make sure it actually implements WorldEditInterface
worldEditInterface = (WorldEditInterface)clazz.getConstructor(AreaShopInterface.class).newInstance(this); // Set our handler
}
} catch(Exception e) {
error("Could not load the handler for WorldEdit (tried to load " + weHandlerVersion + "), report this problem to the author: " + ExceptionUtils.getStackTrace(e));
shutdownOnError();
return;
}
// Load WorldGuard
try {
Class<?> clazz = Class.forName("me.wiefferink.areashop.adapters.plugins.WorldGuardHandler");
// Check if we have a NMSHandler class at that location.
if(WorldGuardInterface.class.isAssignableFrom(clazz)) { // Make sure it actually implements WorldGuardInterface
worldGuardInterface = (WorldGuardInterface)clazz.getConstructor(AreaShopInterface.class).newInstance(this); // Set our handler
}
} catch(Exception e) {
error("Could not load the handler for WorldGuard, report this problem to the author:" + ExceptionUtils.getStackTrace(e));
shutdownOnError();
return;
}
AreaShopModule asModule = new AreaShopModule(this, nms, worldEditInterface, worldGuardInterface, signErrorLogger, platformModule, dependencyModule);
injector = Guice.createInjector(Stage.PRODUCTION, new BukkitModule(getServer()), asModule);
// Load all data from files and check versions
fileManager = injector.getInstance(IFileManager.class);
managers.add((FileManager) fileManager);
boolean loadFilesResult = fileManager.loadFiles(false);
if (!loadFilesResult) {
shutdownOnError();
return;
}
setupLanguageManager();
performLanguageMigrations();
setupNewLanguageSystem((ForwardingMessageBridge) injector.getInstance(MessageBridge.class), fileManager);
featureManager = injector.getInstance(FeatureManager.class);
featureManager.initializeFeatures(injector);
managers.add(featureManager);
signManager = new SignManager();
managers.add(signManager);
// Register the event listeners
getServer().getPluginManager().registerEvents(new PlayerLoginLogoutListener(this, messageBridge), this);
setupTasks();
// Startup the CommandManager (registers itself for the command)
commandManager = injector.getInstance(CommandManager.class);
managers.add(commandManager);
// Create a signLinkerManager
signLinkerManager = injector.getInstance(SignLinkerManager.class);
managers.add(signLinkerManager);
// Register dynamic permission (things declared in config)
registerDynamicPermissions();
// Don't initialize the updatechecker if disabled in the config
if(getConfig().getBoolean("checkForUpdates")) {
githubUpdateCheck = new GithubUpdateCheck(
this,
"md5sha256",
"AreaShop"
).withVersionComparator((latestVersion, currentVersion) ->
!cleanVersion(latestVersion).equals(cleanVersion(currentVersion))
).checkUpdate(result -> {
AreaShop.debug("Update check result:", result);
if(!result.hasUpdate()) {
return;
}
AreaShop.info("Update from AreaShop V" + cleanVersion(result.getCurrentVersion()) + " to AreaShop V" + cleanVersion(result.getLatestVersion()) + " available, get the latest version at https://github.com/md5sha256/releases");
for(Player player : Utils.getOnlinePlayers()) {
notifyUpdate(player);
}
});
}
}
/**
* Notify a player about an update if he wants notifications about it and an update is available.
* @param sender CommandSender to notify
*/
public void notifyUpdate(CommandSender sender) {
if(githubUpdateCheck != null && githubUpdateCheck.hasUpdate() && sender.hasPermission("areashop.notifyupdate")) {
messageBridge.message(sender, "update-playerNotify", cleanVersion(githubUpdateCheck.getCurrentVersion()), cleanVersion(githubUpdateCheck.getLatestVersion()));
}
}
private void shutdownOnError() {
error("The plugin has not started, fix the errors listed above");
getServer().getPluginManager().disablePlugin(this);
}
/**
* Cleanup a version number.
* @param version Version to clean
* @return Cleaned up version (removed prefixes and suffixes)
*/
private String cleanVersion(String version) {
version = version.toLowerCase();
// Strip 'v' as used on Github tags
if(version.startsWith("v")) {
version = version.substring(1);
}
// Strip build number as used by Jenkins
if(version.contains("#")) {
version = version.substring(0, version.indexOf("#"));
}
return version;
}
/**
* Called on shutdown or reload of the server.
*/
@Override
public void onDisable() {
Bukkit.getServer().getScheduler().cancelTasks(this);
// Cleanup managers
for(Manager manager : managers) {
manager.shutdown();
}
signErrorLogger.saveIfDirty();
HandlerList.unregisterAll(this);
}
/**
* Indicates if the plugin is ready to be used.
* @return true if the plugin is ready, false otherwise
*/
public boolean isReady() {
return ready;
}
/**
* Set if the plugin is ready to be used or not (not to be used from another plugin!).
* @param ready Indicate if the plugin is ready to be used
*/
public void setReady(boolean ready) {
this.ready = ready;
}
/**
* Set if the plugin should output debug messages (loaded from config normally).
* @param debug Indicates if the plugin should output debug messages or not
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
private void performLanguageMigrations() {
boolean migrateExisting = this.getConfig().getBoolean("migrateLanguages", false);
if (!migrateExisting) {
return;
}
getLogger().info("Performing language migration");
List<String> chatPrefix = getConfig().getStringList("chatPrefix");
List<String> convertedChatPrefix = LanguageConverter.convertRawList(chatPrefix);
if (convertedChatPrefix.size() == 1) {
getConfig().set("chatPrefixMigrated", convertedChatPrefix.get(0));
} else {
getConfig().set("chatPrefixMigrated", convertedChatPrefix);
}
saveConfig();
File existingLanguages = getDataFolder().toPath().resolve(languageFolder).toFile();
File[] langFiles = existingLanguages.listFiles(file -> file.getName().endsWith(".yml"));
if (langFiles == null) {
return;
}
Path migrationPath = getDataFolder().toPath().resolve("lang-migrated");
migrationPath.toFile().mkdir();
for (File file : langFiles) {
File toMigrate = migrationPath.resolve(file.getName()).toFile();
try {
LanguageConverter.performConversion(file, toMigrate);
} catch (IOException ex) {
getLogger().log(Level.SEVERE, "Failed to perform migration for lang: " + file.getName(), ex);
}
}
getLogger().info("Language migration complete!");
}
/**
* Setup a new LanguageManager.
*/
private void setupLanguageManager() {
String language = getConfig().getString("language");
languageManager = new LanguageManager(
this,
languageFolder,
language,
"EN",
chatprefix
);
}
private void setupNewLanguageSystem(ForwardingMessageBridge messageBridge, IFileManager fileManager) {
AdventureLanguageManager languageManager = setupAdventureLanguageManager(fileManager);
AdventureMessageRenderer messageRenderer = new AdventureMessageRenderer(languageManager);
MessageBridge delegate = provideMessageBridge(fileManager, messageRenderer);
messageBridge.delegate(delegate);
}
private MessageBridge provideMessageBridge(IFileManager fileManager, AdventureMessageRenderer messageRenderer) {
YamlConfiguration config = fileManager.getConfig();
if (!config.getBoolean("useMiniMessage", false)) {
return new LegacyMessageBridge();
}
return new AdventureMessageBridge(messageRenderer);
}
private AdventureLanguageManager setupAdventureLanguageManager(IFileManager fileManager) {
YamlConfiguration config = fileManager.getConfig();
String selectedLang = config.getString("language");
String fallbackLang = "EN";
AdventureLanguageManager languageManager = new AdventureLanguageManager(selectedLang, fallbackLang);
String rawPrefix = config.getString("chatPrefixMigrated", "");
if (!rawPrefix.isEmpty()) {
languageManager.chatPrefix(rawPrefix);
}
File migratedRoot = getDataFolder().toPath().resolve("lang-migrated").toFile();
if (!migratedRoot.isDirectory()) {
return languageManager;
}
File[] langFiles = migratedRoot.listFiles(file -> file.getName().endsWith(".yml"));
if (langFiles == null) {
return languageManager;
}
for (File langFile : langFiles) {
String language = langFile.getName().substring(0, 2);
Map<String, String> values;
try {
ConfigurationNode root = YamlConfigurationLoader.builder().file(langFile).build().load();
values = loadAdventureLanguage(root);
} catch (IOException ex) {
ex.printStackTrace();
error("Failed to setup adventure language manager");
getServer().getPluginManager().disablePlugin(this);
return languageManager;
}
languageManager.loadValues(language, values);
}
return languageManager;
}
private Map<String, String> loadAdventureLanguage(ConfigurationNode root) throws ConfigurateException {
Map<String, String> values = new HashMap<>();
root.visit((ConfigurationVisitor.Stateless<ConfigurateException>)(node) -> {
if (node.isMap()) {
return;
}
Object key = node.key();
if (key == null) {
return;
}
if (node.isList()) {
List<String> list = node.getList(String.class);
if (list == null) {
return;
}
StringJoiner joiner = new StringJoiner("\n");
for (String s : list) {
joiner.add(s);
}
values.put(key.toString(),joiner.toString());
}
String message = node.getString();
if (message == null) {
return;
}
values.put(key.toString(), message);
});
return values;
}
public boolean useMiniMessage() {
return getConfig().getBoolean("useMiniMessage", false);
}
/**
* Set the chatprefix to use in the chat (loaded from config normally).
* @param chatprefix The string to use in front of chat messages (supports formatting codes)
*/
public void setChatprefix(List<String> chatprefix) {
this.chatprefix = chatprefix;
}
public NMS getNms() {
return nms;
}
public SignErrorLogger getSignErrorLogger() {
return signErrorLogger;
}
/**
* Function to get the WorldGuard plugin.
* @return WorldGuardPlugin
*/
@Override
public WorldGuardPlugin getWorldGuard() {
return worldGuard;
}
/**
* Function to get WorldGuardInterface for version dependent things.
* @return WorldGuardInterface
*/
public WorldGuardInterface getWorldGuardHandler() {
return this.worldGuardInterface;
}
@Nonnull
public SignManager getSignManager() {
return signManager;
}
/**
* Get the RegionManager.
* @param world World to get the RegionManager for
* @return RegionManager for the given world, if there is one, otherwise null
*/
public RegionManager getRegionManager(World world) {
return this.worldGuardInterface.getRegionManager(world);
}
/**
* Function to get the WorldEdit plugin.
* @return WorldEditPlugin
*/
@Override
public WorldEditPlugin getWorldEdit() {
return worldEdit;
}
@Override
public Plugin pluginInstance() {
return this;
}
/**
* Function to get WorldGuardInterface for version dependent things.
* @return WorldGuardInterface
*/
public WorldEditInterface getWorldEditHandler() {
return this.worldEditInterface;
}
/**
* Function to get the LanguageManager.
* @return the LanguageManager
*/
public LanguageManager getLanguageManager() {
return languageManager;
}
/**
* Function to get the CommandManager.
* @return the CommandManager
*/
@Nonnull
@Override
public CommandManager getCommandManager() {
return commandManager;
}
/**
* Get the SignLinkerManager.
* Handles sign linking mode.
* @return The SignLinkerManager
*/
@Nonnull
@Override
public SignLinkerManager getSignlinkerManager() {
return signLinkerManager;
}
/**
* Get the FeatureManager.
* Manages region specific features.
* @return The FeatureManager
*/
@Nonnull
@Override
public FeatureManager getFeatureManager() {
return featureManager;
}
/**
* Function to get the Vault plugin.
* @return Economy
*/
@Deprecated
private Economy getEconomy() {
RegisteredServiceProvider<Economy> economy = getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
if(economy == null) {
error("There is no economy provider to support Vault, make sure you installed an economy plugin");
return null;
}
return economy.getProvider();
}
/**
* Get the Vault permissions provider.
* @return Vault permissions provider
*/
@Deprecated
private net.milkbowl.vault.permission.Permission getPermissionProvider() {
RegisteredServiceProvider<net.milkbowl.vault.permission.Permission> permissionProvider = getServer().getServicesManager().getRegistration(net.milkbowl.vault.permission.Permission.class);
if (permissionProvider == null) {
return null;
}
return permissionProvider.getProvider();
}
/**
* Check for a permission of a (possibly offline) player.
* @param offlinePlayer OfflinePlayer to check
* @param permission Permission to check
* @return true if the player has the permission, false if the player does not have permission or, is offline and there is not Vault-compatible permission plugin
*/
public boolean hasPermission(OfflinePlayer offlinePlayer, String permission) {
// Online, return through Bukkit
if(offlinePlayer.getPlayer() != null) {
return offlinePlayer.getPlayer().hasPermission(permission);
}
// Resolve while offline if possible
net.milkbowl.vault.permission.Permission permissionProvider = getPermissionProvider();
if(permissionProvider != null) {
// TODO: Should we provide a world here?
return permissionProvider.playerHas(null, offlinePlayer, permission);
}
// Player offline and no offline permission provider available, safely say that there is no permission
return false;
}
/**
* Method to get the FileManager (loads/save regions and can be used to get regions).
* @return The fileManager
*/
public IFileManager getFileManager() {
return fileManager;
}
/**
* Register dynamic permissions controlled by config settings.
*/
private void registerDynamicPermissions() {
// Register limit groups of amount of regions a player can have
ConfigurationSection section = getConfig().getConfigurationSection("limitGroups");
if(section == null) {
return;
}
for(String group : section.getKeys(false)) {
if(!"default".equals(group)) {
Permission perm = new Permission("areashop.limits." + group);
try {
Bukkit.getPluginManager().addPermission(perm);
} catch(IllegalArgumentException e) {
warn("Could not add the following permission to be used as limit: " + perm.getName());
}
}
}
Bukkit.getPluginManager().recalculatePermissionDefaults(Bukkit.getPluginManager().getPermission("playerwarps.limits"));
}
/**
* Register all required tasks.
*/
private void setupTasks() {
long signLogTicks = Utils.millisToTicks(TimeUnit.MINUTES.toMillis(5));
Bukkit.getScheduler().runTaskTimerAsynchronously(this,
() -> signErrorLogger.saveIfDirty(),
signLogTicks,
signLogTicks);
// Rent expiration timer
long expirationCheck = Utils.millisToTicks(Utils.getDurationFromSecondsOrString("expiration.delay"));
final AreaShop finalPlugin = this;
if(expirationCheck > 0) {
Do.syncTimer(expirationCheck, () -> {
if(isReady()) {
finalPlugin.getFileManager().checkRents();
AreaShop.debugTask("Checking rent expirations...");
} else {
AreaShop.debugTask("Skipped checking rent expirations, plugin not ready");
}
});
}
// Inactive unrenting/selling timer
long inactiveCheck = Utils.millisToTicks(Utils.getDurationFromMinutesOrString("inactive.delay"));
if(inactiveCheck > 0) {
Do.syncTimer(inactiveCheck, () -> {
if(isReady()) {
finalPlugin.getFileManager().checkForInactiveRegions();
AreaShop.debugTask("Checking for regions with players that are inactive too long...");
} else {
AreaShop.debugTask("Skipped checking for regions of inactive players, plugin not ready");
}
});
}
// Periodic updating of signs for timeleft tags
long periodicUpdate = Utils.millisToTicks(Utils.getDurationFromSecondsOrString("signs.delay"));
if(periodicUpdate > 0) {
Do.syncTimer(periodicUpdate, () -> {
if(isReady()) {
finalPlugin.getFileManager().performPeriodicSignUpdate();
AreaShop.debugTask("Performing periodic sign update...");
} else {
AreaShop.debugTask("Skipped performing periodic sign update, plugin not ready");
}
});
}
// Saving regions and group settings
long saveFiles = Utils.millisToTicks(Utils.getDurationFromMinutesOrString("saving.delay"));
if(saveFiles > 0) {
Do.syncTimer(saveFiles, () -> {
if(isReady()) {
finalPlugin.getFileManager().saveRequiredFiles();
AreaShop.debugTask("Saving required files...");
} else {
AreaShop.debugTask("Skipped saving required files, plugin not ready");
}
});
}
// Sending warnings about rent regions to online players
long expireWarning = Utils.millisToTicks(Utils.getDurationFromMinutesOrString("expireWarning.delay"));
if(expireWarning > 0) {
Do.syncTimer(expireWarning, () -> {
if(isReady()) {
finalPlugin.getFileManager().sendRentExpireWarnings();
AreaShop.debugTask("Sending rent expire warnings...");
} else {
AreaShop.debugTask("Skipped sending rent expire warnings, plugin not ready");
}
});
}
// Update all regions on startup
if(getConfig().getBoolean("updateRegionsOnStartup")) {
Do.syncLater(20, () -> {
finalPlugin.getFileManager().updateAllRegions();
AreaShop.debugTask("Updating all regions at startup...");
});
}
}
public @NotNull MessageBridge messageBridge() {
return this.messageBridge;
}
/**
* Return the config.
*/
@Override
public YamlConfiguration getConfig() {
return fileManager.getConfig();
}
/**
* Sends an debug message to the console.
* @param message The message that should be printed to the console
*/
public static void debug(Object... message) {
if(AreaShop.getInstance().debug) {
info("Debug: " + StringUtils.join(message, " "));
}
}
/**
* Non-static debug to use as implementation of the interface.
* @param message Object parts of the message that should be logged, toString() will be used
*/
@Override
public void debugI(Object... message) {
AreaShop.debug(StringUtils.join(message, " "));
}
@Override
public void debugI(Supplier<String> message) {
if (debug) {
info("Debug: " + message.get());
}
}
/**
* Print an information message to the console.
* @param message The message to print
*/
public static void info(Object... message) {
AreaShop.getInstance().getLogger().info(() -> StringUtils.join(message, " "));
}
/**
* Print a warning to the console.
* @param message The message to print
*/
public static void warn(Object... message) {
AreaShop.getInstance().getLogger().warning(() -> StringUtils.join(message, " "));
}
/**
* Print an error to the console.
* @param message The message to print
*/
public static void error(Object... message) {
AreaShop.getInstance().getLogger().severe(() -> StringUtils.join(message, " "));
}
/**
* Print debug message for periodic task.
* @param message The message to print
*/
public static void debugTask(Object... message) {
if(AreaShop.getInstance().getConfig().getBoolean("debugTask")) {
AreaShop.debug(StringUtils.join(message, " "));
}
}
/**
* Reload all files of the plugin and update all regions.
* @param confirmationReceiver The CommandSender which should be notified when complete, null for nobody
*/
public void reload(final CommandSender confirmationReceiver) {
setReady(false);
fileManager.saveRequiredFilesAtOnce();
fileManager.loadFiles(true);
setupLanguageManager();
messageBridge.message(confirmationReceiver, "reload-reloading");
fileManager.checkRents();
fileManager.updateAllRegions(confirmationReceiver);
}
/**
* Reload all files of the plugin and update all regions.
*/
public void reload() {
reload(null);
}
}
@@ -0,0 +1,39 @@
package me.wiefferink.areashop;
import me.wiefferink.areashop.features.signs.SignManager;
import me.wiefferink.areashop.interfaces.AreaShopInterface;
import me.wiefferink.areashop.managers.CommandManager;
import me.wiefferink.areashop.managers.FeatureManager;
import me.wiefferink.areashop.managers.SignLinkerManager;
import javax.annotation.Nonnull;
public interface AreaShopApi extends AreaShopInterface {
/**
* Function to get the CommandManager.
*
* @return the CommandManager
*/
@Nonnull CommandManager getCommandManager();
/**
* Get the SignLinkerManager.
* Handles sign linking mode.
*
* @return The SignLinkerManager
*/
@Nonnull SignLinkerManager getSignlinkerManager();
/**
* Get the FeatureManager.
* Manages region specific features.
*
* @return The FeatureManager
*/
@Nonnull FeatureManager getFeatureManager();
@Nonnull SignManager getSignManager();
}
@@ -0,0 +1,25 @@
package me.wiefferink.areashop;
import io.github.md5sha256.areashop.MessageWrapper;
public interface MessageBridge {
/**
* Send a message to a target without a prefix.
*
* @param target The target to send the message to
* @param key The key of the language string
* @param replacements The replacements to insert in the message
*/
void messageNoPrefix(Object target, String key, Object... replacements);
/**
* Send a message to a target, prefixed by the default chat prefix.
*
* @param target The target to send the message to
* @param key The key of the language string
* @param replacements The replacements to insert in the message
*/
void message(Object target, String key, Object... replacements);
void message(Object target, MessageWrapper message);
}
@@ -0,0 +1,345 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.events.ask.BuyingRegionEvent;
import me.wiefferink.areashop.events.ask.RentingRegionEvent;
import me.wiefferink.areashop.events.notify.BoughtRegionEvent;
import me.wiefferink.areashop.events.notify.RentedRegionEvent;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionFactory;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.bukkitdo.Do;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
@Singleton
public class AddCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private AreaShop plugin;
@Inject
private WorldEditInterface worldEditInterface;
@Inject
private WorldGuardInterface worldGuardInterface;
@Inject
private RegionFactory regionFactory;
@Override
public String getCommandStart() {
return "areashop add";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.createrent")
|| target.hasPermission("areashop.createrent.member")
|| target.hasPermission("areashop.createrent.owner")
|| target.hasPermission("areashop.createbuy")
|| target.hasPermission("areashop.createbuy.member")
|| target.hasPermission("areashop.createbuy.owner")) {
return "help-add";
}
return null;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
if(!sender.hasPermission("areashop.createrent")
&& !sender.hasPermission("areashop.createrent.member")
&& !sender.hasPermission("areashop.createrent.owner")
&& !sender.hasPermission("areashop.createbuy")
&& !sender.hasPermission("areashop.createbuy.member")
&& !sender.hasPermission("areashop.createbuy.owner")) {
messageBridge.message(sender, "add-noPermission");
return;
}
if(args.length < 2 || args[1] == null || (!"rent".equalsIgnoreCase(args[1]) && !"buy".equalsIgnoreCase(args[1]))) {
messageBridge.message(sender, "add-help");
return;
}
Map<String, ProtectedRegion> regions = new HashMap<>();
World world;
Player player = null;
if(sender instanceof Player) {
player = (Player)sender;
}
if(args.length == 2) {
if(player == null) {
messageBridge.message(sender, "cmd-weOnlyByPlayer");
return;
}
WorldEditSelection selection = worldEditInterface.getPlayerSelection(player);
if(selection == null) {
messageBridge.message(player, "cmd-noSelection");
return;
}
world = selection.getWorld();
regions = Utils.getWorldEditRegionsInSelection(selection).stream().collect(Collectors.toMap(ProtectedRegion::getId, region -> region));
if(regions.isEmpty()) {
messageBridge.message(player, "cmd-noWERegionsFound");
return;
}
} else {
if(player != null) {
if(args.length == 4) {
world = Bukkit.getWorld(args[3]);
if(world == null) {
messageBridge.message(sender, "add-incorrectWorld", args[3]);
return;
}
} else {
world = ((Player)sender).getWorld();
}
} else {
if(args.length < 4) {
messageBridge.message(sender, "add-specifyWorld");
return;
} else {
world = Bukkit.getWorld(args[3]);
if(world == null) {
messageBridge.message(sender, "add-incorrectWorld", args[3]);
return;
}
}
}
ProtectedRegion region = plugin.getRegionManager(world).getRegion(args[2]);
if(region == null) {
messageBridge.message(sender, "cmd-noRegion", args[2]);
return;
}
regions.put(args[2], region);
}
final boolean isRent = "rent".equalsIgnoreCase(args[1]);
final Player finalPlayer = player;
AreaShop.debug("Starting add task with " + regions.size() + " regions");
TreeSet<GeneralRegion> regionsSuccess = new TreeSet<>();
TreeSet<GeneralRegion> regionsAlready = new TreeSet<>();
TreeSet<GeneralRegion> regionsAlreadyOtherWorld = new TreeSet<>();
TreeSet<GeneralRegion> regionsRentCancelled = new TreeSet<>(); // Denied by an event listener
TreeSet<GeneralRegion> regionsBuyCancelled = new TreeSet<>(); // Denied by an event listener
TreeSet<String> namesBlacklisted = new TreeSet<>();
TreeSet<String> namesNoPermission = new TreeSet<>();
TreeSet<String> namesAddCancelled = new TreeSet<>(); // Denied by an event listener
Do.forAll(
plugin.getConfig().getInt("adding.regionsPerTick"),
regions.entrySet(),
regionEntry -> {
String regionName = regionEntry.getKey();
ProtectedRegion region = regionEntry.getValue();
// Determine if the player is an owner or member of the region
boolean isMember = finalPlayer != null && worldGuardInterface.containsMember(region, finalPlayer.getUniqueId());
boolean isOwner = finalPlayer != null && worldGuardInterface.containsOwner(region, finalPlayer.getUniqueId());
String type;
if(isRent) {
type = "rent";
} else {
type = "buy";
}
IFileManager.AddResult result = plugin.getFileManager().checkRegionAdd(sender, region, world, isRent ? GeneralRegion.RegionType.RENT : GeneralRegion.RegionType.BUY);
if(result == IFileManager.AddResult.ALREADYADDED) {
regionsAlready.add(plugin.getFileManager().getRegion(regionName));
} else if(result == IFileManager.AddResult.ALREADYADDEDOTHERWORLD) {
regionsAlreadyOtherWorld.add(plugin.getFileManager().getRegion(regionName));
} else if(result == IFileManager.AddResult.BLACKLISTED) {
namesBlacklisted.add(regionName);
} else if(result == IFileManager.AddResult.NOPERMISSION) {
namesNoPermission.add(regionName);
} else {
// Check if the player should be landlord
boolean landlord = (!sender.hasPermission("areashop.create" + type)
&& ((sender.hasPermission("areashop.create" + type + ".owner") && isOwner)
|| (sender.hasPermission("areashop.create" + type + ".member") && isMember)));
List<UUID> existing = new ArrayList<>();
existing.addAll(worldGuardInterface.getOwners(region).asUniqueIdList());
existing.addAll(worldGuardInterface.getMembers(region).asUniqueIdList());
AreaShop.debug("regionAddLandlordStatus:", regionName,
"landlord:", landlord,
"existing:", existing,
"isMember:", isMember,
"isOwner:", isOwner,
"createPermission:", sender.hasPermission("areashop.create" + type),
"ownerPermission:", sender.hasPermission("areashop.create" + type + ".owner"),
"memberPermission:", sender.hasPermission("areashop.create" + type + ".member"));
if(isRent) {
RentRegion rent = regionFactory.createRentRegion(regionName, world);
// Set landlord
if(landlord) {
rent.setLandlord(finalPlayer.getUniqueId(), finalPlayer.getName());
}
AddingRegionEvent event = plugin.getFileManager().addRegion(rent);
if (event.isCancelled()) {
namesAddCancelled.add(rent.getName());
return;
}
rent.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
rent.update();
// Add existing owners/members if any
if(!landlord && !existing.isEmpty()) {
UUID rentBy = existing.remove(0);
OfflinePlayer rentByPlayer = Bukkit.getOfflinePlayer(rentBy);
RentingRegionEvent rentingRegionEvent = new RentingRegionEvent(rent, rentByPlayer, false);
Bukkit.getPluginManager().callEvent(rentingRegionEvent);
if(rentingRegionEvent.isCancelled()) {
regionsRentCancelled.add(rent);
} else {
// Add values to the rent and send it to FileManager
rent.setRentedUntil(Calendar.getInstance().getTimeInMillis() + rent.getDuration());
rent.setRenter(rentBy);
rent.updateLastActiveTime();
// Fire schematic event and updated times extended
rent.handleSchematicEvent(GeneralRegion.RegionEvent.RENTED);
// Add others as friends
for(UUID friend : existing) {
rent.getFriendsFeature().addFriend(friend, null);
}
rent.notifyAndUpdate(new RentedRegionEvent(rent, false));
}
}
regionsSuccess.add(rent);
} else {
BuyRegion buy = regionFactory.createBuyRegion(regionName, world);
// Set landlord
if(landlord) {
buy.setLandlord(finalPlayer.getUniqueId(), finalPlayer.getName());
}
AddingRegionEvent event = plugin.getFileManager().addRegion(buy);
if (event.isCancelled()) {
namesAddCancelled.add(buy.getName());
return;
}
buy.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
buy.update();
// Add existing owners/members if any
if(!landlord && !existing.isEmpty()) {
UUID buyBy = existing.remove(0);
OfflinePlayer buyByPlayer = Bukkit.getOfflinePlayer(buyBy);
BuyingRegionEvent buyingRegionEvent = new BuyingRegionEvent(buy, buyByPlayer);
Bukkit.getPluginManager().callEvent(buyingRegionEvent);
if(buyingRegionEvent.isCancelled()) {
regionsBuyCancelled.add(buy);
} else {
// Set the owner
buy.setBuyer(buyBy);
buy.updateLastActiveTime();
// Update everything
buy.handleSchematicEvent(GeneralRegion.RegionEvent.BOUGHT);
// Add others as friends
for (UUID friend : existing) {
buy.getFriendsFeature().addFriend(friend, null);
}
buy.notifyAndUpdate(new BoughtRegionEvent(buy));
}
}
regionsSuccess.add(buy);
}
}
},
() -> {
if(!regionsSuccess.isEmpty()) {
messageBridge.message(sender, "add-success", args[1], Utils.combinedMessage(regionsSuccess, "region"));
}
if(!regionsAlready.isEmpty()) {
messageBridge.message(sender, "add-failed", Utils.combinedMessage(regionsAlready, "region"));
}
if(!regionsAlreadyOtherWorld.isEmpty()) {
messageBridge.message(sender, "add-failedOtherWorld", Utils.combinedMessage(regionsAlreadyOtherWorld, "region"));
}
if(!regionsRentCancelled.isEmpty()) {
messageBridge.message(sender, "add-rentCancelled", Utils.combinedMessage(regionsRentCancelled, "region"));
}
if(!regionsBuyCancelled.isEmpty()) {
messageBridge.message(sender, "add-buyCancelled", Utils.combinedMessage(regionsBuyCancelled, "region"));
}
if(!namesBlacklisted.isEmpty()) {
messageBridge.message(sender, "add-blacklisted", Utils.createCommaSeparatedList(namesBlacklisted));
}
if(!namesNoPermission.isEmpty()) {
messageBridge.message(sender, "add-noPermissionRegions", Utils.createCommaSeparatedList(namesNoPermission));
messageBridge.message(sender, "add-noPermissionOwnerMember");
}
if(!namesAddCancelled.isEmpty()) {
messageBridge.message(sender, "add-rentCancelled", Utils.createCommaSeparatedList(namesAddCancelled));
}
}
);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
if(sender.hasPermission("areashop.createrent")) {
result.add("rent");
}
if(sender.hasPermission("areashop.createbuy")) {
result.add("buy");
}
} else if(toComplete == 3) {
if(sender instanceof Player player) {
if(sender.hasPermission("areashop.createrent") || sender.hasPermission("areashop.createbuy")) {
for(ProtectedRegion region : plugin.getRegionManager(player.getWorld()).getRegions().values()) {
result.add(region.getId());
}
}
}
}
return result;
}
}
@@ -0,0 +1,148 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.adapters.platform.OfflinePlayerHelper;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.BukkitSchedulerExecutor;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class AddfriendCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private Plugin plugin;
@Inject
private OfflinePlayerHelper offlinePlayerHelper;
@Inject
private BukkitSchedulerExecutor executor;
@Override
public String getCommandStart() {
return "areashop addfriend";
}
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.addfriendall") || target.hasPermission("areashop.addfriend")) {
return "help-addFriend";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("areashop.addfriend") && !sender.hasPermission("areashop.addfriendall")) {
messageBridge.message(sender, "addfriend-noPermission");
return;
}
if (args.length < 2) {
messageBridge.message(sender, "addfriend-help");
return;
}
GeneralRegion region;
if (args.length == 2) {
if (sender instanceof Player player) {
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(player.getLocation());
if (regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
region = regions.get(0);
}
} else {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
region = fileManager.getRegion(args[2]);
if (region == null) {
messageBridge.message(sender, "cmd-notRegistered", args[2]);
return;
}
}
if (sender.hasPermission("areashop.addfriendall")) {
if ((region instanceof RentRegion rentRegion && !rentRegion.isRented())
|| (region instanceof BuyRegion buyRegion && !buyRegion.isSold())) {
messageBridge.message(sender, "addfriend-noOwner", region);
return;
}
this.offlinePlayerHelper.lookupOfflinePlayerAsync(args[1]).thenAcceptAsync(friend -> {
if (friend.hasPlayedBefore() && !friend.isOnline() && !plugin.getConfig().getBoolean("addFriendNotExistingPlayers")) {
messageBridge.message(sender, "addfriend-notVisited", args[1], region);
} else if (region.getFriendsFeature().getFriends().contains(friend.getUniqueId())) {
messageBridge.message(sender, "addfriend-alreadyAdded", friend.getName(), region);
} else if (region.isOwner(friend.getUniqueId())) {
messageBridge.message(sender, "addfriend-self", friend.getName(), region);
} else if (region.getFriendsFeature().addFriend(friend.getUniqueId(), sender)) {
region.update();
messageBridge.message(sender, "addfriend-successOther", friend.getName(), region);
}
}, this.executor);
return;
}
if (!sender.hasPermission("areashop.addfriend") || !(sender instanceof Player player)) {
messageBridge.message(sender, "addfriend-noPermission", region);
return;
}
if (!region.isOwner(player)) {
messageBridge.message(sender, "addfriend-noPermissionOther", region);
return;
}
this.offlinePlayerHelper.lookupOfflinePlayerAsync(args[1]).thenAcceptAsync(friend -> {
if (friend.getLastPlayed() == 0 && !friend.isOnline() && !plugin.getConfig().getBoolean("addFriendNotExistingPlayers")) {
messageBridge.message(sender, "addfriend-notVisited", args[1], region);
} else if (region.getFriendsFeature().getFriends().contains(friend.getUniqueId())) {
messageBridge.message(sender, "addfriend-alreadyAdded", friend.getName(), region);
} else if (region.isOwner(friend.getUniqueId())) {
messageBridge.message(sender, "addfriend-self", friend.getName(), region);
} else if (region.getFriendsFeature().addFriend(friend.getUniqueId(), sender)) {
region.update();
messageBridge.message(sender, "addfriend-success", friend.getName(), region);
}
}, this.executor);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if (toComplete == 2) {
for (Player player : Utils.getOnlinePlayers()) {
result.add(player.getName());
}
} else if (toComplete == 3) {
result.addAll(fileManager.getRegionNames());
}
return result;
}
}
@@ -0,0 +1,141 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignManager;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.SignUtils;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.BlockIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@Singleton
public class AddsignCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private SignManager signManager;
@Inject
private Plugin plugin;
@Override
public String getCommandStart() {
return "areashop addsign";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.addsign")) {
return "help-addsign";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.addsign")) {
messageBridge.message(sender, "addsign-noPermission");
return;
}
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-onlyByPlayer");
return;
}
// Get the sign
Block block = null;
BlockIterator blockIterator = new BlockIterator(player, 100);
while(blockIterator.hasNext() && block == null) {
Block next = blockIterator.next();
if(next.getType() != Material.AIR) {
block = next;
}
}
if(block == null || !Materials.isSign(block.getType())) {
messageBridge.message(sender, "addsign-noSign");
return;
}
GeneralRegion region;
if(args.length > 1) {
// Get region by argument
region = fileManager.getRegion(args[1]);
if(region == null) {
messageBridge.message(sender, "cmd-notRegistered", args[1]);
return;
}
} else {
// Get region by sign position
List<GeneralRegion> regions = Utils.getImportantRegions(block.getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "addsign-noRegions");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "addsign-couldNotDetect", regions.get(0).getName(), regions.get(1).getName());
return;
}
region = regions.get(0);
}
String profile = null;
if(args.length > 2) {
profile = args[2];
Set<String> profiles = plugin.getConfig().getConfigurationSection("signProfiles").getKeys(false);
if(!profiles.contains(profile)) {
messageBridge.message(sender, "addsign-wrongProfile", Utils.createCommaSeparatedList(profiles), region);
return;
}
}
Optional<RegionSign> optionalRegionSign = signManager.signFromLocation(block.getLocation());
if(optionalRegionSign.isPresent()) {
RegionSign regionSign = optionalRegionSign.get();
messageBridge.message(sender, "addsign-alreadyRegistered", regionSign.getRegion());
return;
}
region.getSignsFeature().addSign(block.getLocation(), block.getType(), SignUtils.getSignFacing(block), profile);
if(profile == null) {
messageBridge.message(sender, "addsign-success", region);
} else {
messageBridge.message(sender, "addsign-successProfile", profile, region);
}
region.update();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result.addAll(fileManager.getRegionNames());
} else if(toComplete == 3) {
result.addAll(plugin.getConfig().getStringList("signProfiles"));
}
return result;
}
}
@@ -0,0 +1,81 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class BuyCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private Plugin plugin;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop buy";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.buy")) {
return "help-buy";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.buy")) {
messageBridge.message(sender, "buy-noPermission");
return;
}
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-onlyByPlayer");
return;
}
if(args.length > 1 && args[1] != null) {
BuyRegion region = fileManager.getBuy(args[1]);
if(region == null) {
messageBridge.message(player, "buy-notBuyable", args[1]);
} else {
region.buy(player);
}
} else {
// get the region by location
List<BuyRegion> regions = Utils.getImportantBuyRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
} else {
regions.get(0).buy(player);
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(BuyRegion region : fileManager.getBuysRef()) {
if(!region.isSold()) {
result.add(region.getName());
}
}
}
return result;
}
}
@@ -0,0 +1,99 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.interactivemessenger.processing.Message;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Abstract class for generalising command classes.
*/
public abstract class CommandAreaShop {
private final Map<String, CommandTime> lastUsed;
protected CommandAreaShop() {
lastUsed = new HashMap<>();
}
/**
* Check if this Command instance can execute the given command and arguments.
* @param command The command to check for execution
* @param args The arguments to check
* @return true if it can execute the command, false otherwise
*/
public boolean canExecute(Command command, String[] args) {
String commandString = command.getName() + " " + StringUtils.join(args, " ");
if(commandString.length() > getCommandStart().length()) {
return commandString.toLowerCase().startsWith(getCommandStart().toLowerCase() + " ");
}
return commandString.toLowerCase().startsWith(getCommandStart().toLowerCase());
}
/**
* Get a list of string to complete a command with (raw list, not matching ones not filtered out).
* @param toComplete The number of the argument that has to be completed
* @param start The already given start of the command
* @param sender The CommandSender that wants to tab complete
* @return A collection with all the possibilities for argument to complete
*/
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
return new ArrayList<>();
}
/**
* Get the argument that comes after the base command that this command reacts to.
* @return The string that should be in front of the command for this class to act
*/
public abstract String getCommandStart();
/**
* Returns the correct help string key to be used on the help page.
* @param target The CommandSender that the help message is for
* @return The help message key according to the permissions of the reciever
*/
public abstract String getHelp(CommandSender target);
/**
* Execute a (sub)command if the conditions are met.
* @param sender The commandSender that executed the command
* @param args The arguments that are given
*/
public abstract void execute(CommandSender sender, String[] args);
/**
* Confirm a command.
* @param sender To confirm it for, or send a message to confirm
* @param args Command args
* @param message Message to send when confirmation is required
* @return true if confirmed, false if confirmation is required
*/
public boolean confirm(CommandSender sender, String[] args, Message message) {
String command = "/" + getCommandStart() + " " + StringUtils.join(args, " ", 1, args.length);
long now = System.currentTimeMillis();
CommandTime last = lastUsed.get(sender.getName());
if(last != null && last.command.equalsIgnoreCase(command) && last.time > (now - 1000 * 60)) {
return true;
}
message.prefix().append(Message.fromKey("confirm-yes").replacements(command)).send(sender);
lastUsed.put(sender.getName(), new CommandTime(command, now));
return false;
}
private static class CommandTime {
public final String command;
public final long time;
CommandTime(String command, long time) {
this.command = command;
this.time = time;
}
}
}
@@ -0,0 +1,160 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.events.ask.DeletingRegionEvent;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
@Singleton
public class DelCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private WorldEditInterface worldEditInterface;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop del";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.destroyrent") || target.hasPermission("areashop.destroybuy") || target.hasPermission("areashop.destroyrent.landlord") || target.hasPermission("areashop.destroybuy.landlord")) {
return "help-del";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.destroybuy")
&& !sender.hasPermission("areashop.destroybuy.landlord")
&& !sender.hasPermission("areashop.destroyrent")
&& !sender.hasPermission("areashop.destroyrent.landlord")) {
messageBridge.message(sender, "del-noPermission");
return;
}
if(args.length < 2) {
// Only players can have a selection
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-weOnlyByPlayer");
return;
}
WorldEditSelection selection = worldEditInterface.getPlayerSelection(player);
if(selection == null) {
messageBridge.message(player, "cmd-noSelection");
return;
}
List<GeneralRegion> regions = Utils.getRegionsInSelection(selection);
if(regions == null || regions.isEmpty()) {
messageBridge.message(player, "cmd-noRegionsFound");
return;
}
// Start removing the regions that he has permission for
ArrayList<String> namesSuccess = new ArrayList<>();
TreeSet<GeneralRegion> regionsFailed = new TreeSet<>();
TreeSet<GeneralRegion> regionsCancelled = new TreeSet<>();
for(GeneralRegion region : regions) {
boolean isLandlord = region.isLandlord(((Player)sender).getUniqueId());
if(region instanceof RentRegion) {
if(!sender.hasPermission("areashop.destroyrent") && !(isLandlord && sender.hasPermission("areashop.destroyrent.landlord"))) {
regionsFailed.add(region);
continue;
}
} else if(region instanceof BuyRegion) {
if(!sender.hasPermission("areashop.destroybuy") && !(isLandlord && sender.hasPermission("areashop.destroybuy.landlord"))) {
regionsFailed.add(region);
continue;
}
}
DeletingRegionEvent event = fileManager.deleteRegion(region, true);
if (event.isCancelled()) {
regionsCancelled.add(region);
} else {
namesSuccess.add(region.getName());
}
}
// Send messages
if(!namesSuccess.isEmpty()) {
messageBridge.message(sender, "del-success", Utils.createCommaSeparatedList(namesSuccess));
}
if(!regionsFailed.isEmpty()) {
messageBridge.message(sender, "del-failed", Utils.combinedMessage(regionsFailed, "region"));
}
if(!regionsCancelled.isEmpty()) {
messageBridge.message(sender, "del-cancelled", Utils.combinedMessage(regionsCancelled, "region"));
}
} else {
GeneralRegion region = fileManager.getRegion(args[1]);
if(region == null) {
messageBridge.message(sender, "cmd-notRegistered", args[1]);
return;
}
boolean isLandlord = sender instanceof Player && region.isLandlord(((Player)sender).getUniqueId());
if(region instanceof RentRegion) {
// Remove the rent if the player has permission
if(sender.hasPermission("areashop.destroyrent") || (isLandlord && sender.hasPermission("areashop.destroyrent.landlord"))) {
DeletingRegionEvent event = fileManager.deleteRegion(region, true);
if (event.isCancelled()) {
messageBridge.message(sender, "general-cancelled", event.getReason());
} else {
messageBridge.message(sender, "destroy-successRent", region);
}
} else {
messageBridge.message(sender, "destroy-noPermissionRent", region);
}
} else if(region instanceof BuyRegion) {
// Remove the buy if the player has permission
if(sender.hasPermission("areashop.destroybuy") || (isLandlord && sender.hasPermission("areashop.destroybuy.landlord"))) {
DeletingRegionEvent event = fileManager.deleteRegion(region, true);
if (event.isCancelled()) {
messageBridge.message(sender, "general-cancelled", event.getReason());
} else {
messageBridge.message(sender, "destroy-successBuy", region);
}
} else {
messageBridge.message(sender, "destroy-noPermissionBuy", region);
}
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result = fileManager.getRegionNames();
}
return result;
}
}
@@ -0,0 +1,153 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.adapters.platform.OfflinePlayerHelper;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.BukkitSchedulerExecutor;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class DelfriendCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private OfflinePlayerHelper offlinePlayerHelper;
@Inject
private BukkitSchedulerExecutor executor;
@Override
public String getCommandStart() {
return "areashop delfriend";
}
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.delfriendall") || target.hasPermission("areashop.delfriend")) {
return "help-delFriend";
}
return null;
}
/**
* Check if a person can remove friends.
*
* @param person The person to check
* @param region The region to check for
* @return true if the person can remove friends, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if (person.hasPermission("areashop.delfriendall")) {
return true;
}
if (person instanceof Player player) {
return region.isOwner(player) && player.hasPermission("areashop.delfriend");
}
return false;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("areashop.delfriend") && !sender.hasPermission("areashop.delfriendall")) {
messageBridge.message(sender, "delfriend-noPermission");
return;
}
if (args.length < 2) {
messageBridge.message(sender, "delfriend-help");
return;
}
GeneralRegion region;
if (args.length == 2) {
if (!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(player.getLocation());
if (regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
region = regions.get(0);
}
} else {
region = fileManager.getRegion(args[2]);
if (region == null) {
messageBridge.message(sender, "cmd-notRegistered", args[2]);
return;
}
}
if (sender.hasPermission("areashop.delfriendall")) {
if ((region instanceof RentRegion rentRegion && !rentRegion.isRented())
|| (region instanceof BuyRegion buyRegion && !buyRegion.isSold())) {
messageBridge.message(sender, "delfriend-noOwner", region);
return;
}
this.offlinePlayerHelper.lookupOfflinePlayerAsync(args[1]).thenAcceptAsync(friend -> {
if (!region.getFriendsFeature().getFriends().contains(friend.getUniqueId())) {
messageBridge.message(sender, "delfriend-notAdded", friend.getName(), region);
return;
}
if (region.getFriendsFeature().deleteFriend(friend.getUniqueId(), sender)) {
region.update();
messageBridge.message(sender, "delfriend-successOther", friend.getName(), region);
}
}, this.executor);
return;
}
if (!sender.hasPermission("areashop.delfriend") || !(sender instanceof Player player)) {
messageBridge.message(sender, "delfriend-noPermission", region);
return;
}
if (!region.isOwner(player)) {
messageBridge.message(sender, "delfriend-noPermissionOther", region);
return;
}
this.offlinePlayerHelper.lookupOfflinePlayerAsync(args[1]).thenAcceptAsync(friend -> {
if (!region.getFriendsFeature().getFriends().contains(friend.getUniqueId())) {
messageBridge.message(sender, "delfriend-notAdded", friend.getName(), region);
} else if (region.getFriendsFeature().deleteFriend(friend.getUniqueId(), sender)) {
region.update();
messageBridge.message(sender, "delfriend-success", friend.getName(), region);
}
}, this.executor);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(Player player : Utils.getOnlinePlayers()) {
result.add(player.getName());
}
} else if(toComplete == 3) {
result.addAll(fileManager.getRegionNames());
}
return result;
}
}
@@ -0,0 +1,91 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignManager;
import me.wiefferink.areashop.tools.Materials;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.BlockIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Singleton
public class DelsignCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private SignManager signManager;
@Override
public String getCommandStart() {
return "areashop delsign";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.delsign")) {
return "help-delsign";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.delsign")) {
messageBridge.message(sender, "delsign-noPermission");
return;
}
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-onlyByPlayer");
return;
}
// Get the sign
Block block = null;
BlockIterator blockIterator = new BlockIterator(player, 100);
while(blockIterator.hasNext() && block == null) {
Block next = blockIterator.next();
if(!next.getType().isAir()) {
block = next;
}
}
if(block == null || !Materials.isSign(block.getType())) {
messageBridge.message(sender, "delsign-noSign");
return;
}
Optional<RegionSign> optionalSign = signManager.removeSign(block.getLocation());
if(optionalSign.isEmpty()) {
messageBridge.message(sender, "delsign-noRegion");
return;
}
RegionSign regionSign = optionalSign.get();
messageBridge.message(sender, "delsign-success", regionSign.getRegion());
regionSign.remove();
// Sometimes the RegionSign data is corrupted. Forcefully set the block to air
block.setType(Material.AIR);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
return new ArrayList<>();
}
}
@@ -0,0 +1,204 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
@Singleton
public class FindCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private Economy economy;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop find";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.find")) {
return "help-find";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.find")) {
messageBridge.message(sender, "find-noPermission");
return;
}
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-onlyByPlayer");
return;
}
if(args.length <= 1 || args[1] == null || (!args[1].equalsIgnoreCase("buy") && !args[1].equalsIgnoreCase("rent"))) {
messageBridge.message(sender, "find-help");
return;
}
double balance = 0.0;
if(economy != null) {
balance = economy.getBalance(player);
}
double maxPrice = 0;
boolean maxPriceSet = false;
RegionGroup group = null;
// Parse optional price argument
if(args.length >= 3) {
try {
maxPrice = Double.parseDouble(args[2]);
maxPriceSet = true;
} catch(NumberFormatException e) {
messageBridge.message(sender, "find-wrongMaxPrice", args[2]);
return;
}
}
// Parse optional group argument
if(args.length >= 4) {
group = fileManager.getGroup(args[3]);
if(group == null) {
messageBridge.message(sender, "find-wrongGroup", args[3]);
return;
}
}
// Find buy regions
if(args[1].equalsIgnoreCase("buy")) {
Collection<BuyRegion> regions = fileManager.getBuysRef();
List<BuyRegion> results = new LinkedList<>();
for(BuyRegion region : regions) {
if(!region.isSold()
&& ((region.getPrice() <= balance && !maxPriceSet) || (region.getPrice() <= maxPrice && maxPriceSet))
&& (group == null || group.isMember(region))
&& (region.getBooleanSetting("general.findCrossWorld") || player.getWorld().equals(region.getWorld()))) {
results.add(region);
}
}
if(!results.isEmpty()) {
// Draw a random one
BuyRegion region = results.get(new Random().nextInt(results.size()));
Message onlyInGroup = Message.empty();
if(group != null) {
onlyInGroup = Message.fromKey("find-onlyInGroup").replacements(args[3]);
}
// Teleport
if(maxPriceSet) {
messageBridge.message(player, "find-successMax", "buy", Utils.formatCurrency(maxPrice), onlyInGroup, region);
} else {
messageBridge.message(player, "find-success", "buy", Utils.formatCurrency(balance), onlyInGroup, region);
}
region.getTeleportFeature().teleportPlayer(player, region.getBooleanSetting("general.findTeleportToSign"), false);
} else {
Message onlyInGroup = Message.empty();
if(group != null) {
onlyInGroup = Message.fromKey("find-onlyInGroup").replacements(args[3]);
}
if(maxPriceSet) {
messageBridge.message(player, "find-noneFoundMax", "buy", Utils.formatCurrency(maxPrice), onlyInGroup);
} else {
messageBridge.message(player, "find-noneFound", "buy", Utils.formatCurrency(balance), onlyInGroup);
}
}
}
// Find rental regions
else {
Collection<RentRegion> regions = fileManager.getRentsRef();
List<RentRegion> results = new LinkedList<>();
for(RentRegion region : regions) {
if(!region.isRented()
&& ((region.getPrice() <= balance && !maxPriceSet) || (region.getPrice() <= maxPrice && maxPriceSet))
&& (group == null || group.isMember(region))
&& (region.getBooleanSetting("general.findCrossWorld") || player.getWorld().equals(region.getWorld()))) {
results.add(region);
}
}
if(!results.isEmpty()) {
// Draw a random one
RentRegion region = results.get(new Random().nextInt(results.size()));
Message onlyInGroup = Message.empty();
if(group != null) {
onlyInGroup = Message.fromKey("find-onlyInGroup").replacements(args[3]);
}
// Teleport
if(maxPriceSet) {
messageBridge.message(player, "find-successMax", "rent", Utils.formatCurrency(maxPrice), onlyInGroup, region);
} else {
messageBridge.message(player, "find-success", "rent", Utils.formatCurrency(balance), onlyInGroup, region);
}
region.getTeleportFeature().teleportPlayer(player, region.getBooleanSetting("general.findTeleportToSign"), false);
} else {
Message onlyInGroup = Message.empty();
if(group != null) {
onlyInGroup = Message.fromKey("find-onlyInGroup").replacements(args[3]);
}
if(maxPriceSet) {
messageBridge.message(player, "find-noneFoundMax", "rent", Utils.formatCurrency(maxPrice), onlyInGroup);
} else {
messageBridge.message(player, "find-noneFound", "rent", Utils.formatCurrency(balance), onlyInGroup);
}
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
result.add("buy");
result.add("rent");
}
return result;
}
}
@@ -0,0 +1,129 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionFactory;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
@Singleton
public class GroupaddCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private RegionFactory regionFactory;
@Inject
private WorldEditInterface worldEditInterface;
@Override
public String getCommandStart() {
return "areashop groupadd";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.groupadd")) {
return "help-groupadd";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.groupadd")) {
messageBridge.message(sender, "groupadd-noPermission");
return;
}
if(args.length < 2 || args[1] == null) {
messageBridge.message(sender, "groupadd-help");
return;
}
RegionGroup group = fileManager.getGroup(args[1]);
if(group == null) {
group = regionFactory.createRegionGroup(args[1]);
fileManager.addGroup(group);
}
if(args.length == 2) {
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-weOnlyByPlayer");
return;
}
WorldEditSelection selection = worldEditInterface.getPlayerSelection(player);
if(selection == null) {
messageBridge.message(player, "cmd-noSelection");
return;
}
List<GeneralRegion> regions = Utils.getRegionsInSelection(selection);
if(regions.isEmpty()) {
messageBridge.message(player, "cmd-noRegionsFound");
return;
}
TreeSet<GeneralRegion> regionsSuccess = new TreeSet<>();
TreeSet<GeneralRegion> regionsFailed = new TreeSet<>();
for(GeneralRegion region : regions) {
if(group.addMember(region)) {
regionsSuccess.add(region);
} else {
regionsFailed.add(region);
}
}
if(!regionsSuccess.isEmpty()) {
messageBridge.message(player, "groupadd-weSuccess", group.getName(), Utils.combinedMessage(regionsSuccess, "region"));
}
if(!regionsFailed.isEmpty()) {
messageBridge.message(player, "groupadd-weFailed", group.getName(), Utils.combinedMessage(regionsFailed, "region"));
}
// Update all regions, this does it in a task, updating them without lag
fileManager.updateRegions(new ArrayList<>(regionsSuccess), player);
} else {
GeneralRegion region = fileManager.getRegion(args[2]);
if(region == null) {
messageBridge.message(sender, "cmd-notRegistered", args[2]);
return;
}
if(group.addMember(region)) {
region.update();
messageBridge.message(sender, "groupadd-success", group.getName(), group.getMembers().size(), region);
} else {
messageBridge.message(sender, "groupadd-failed", group.getName(), region);
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result = fileManager.getGroupNames();
} else if(toComplete == 3) {
result = fileManager.getRegionNames();
}
return result;
}
}
@@ -0,0 +1,130 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionFactory;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
@Singleton
public class GroupdelCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private RegionFactory regionFactory;
@Inject
private WorldEditInterface worldEditInterface;
@Override
public String getCommandStart() {
return "areashop groupdel";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.groupdel")) {
return "help-groupdel";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.groupdel")) {
messageBridge.message(sender, "groupdel-noPermission");
return;
}
if(args.length < 2 || args[1] == null) {
messageBridge.message(sender, "groupdel-help");
return;
}
RegionGroup group = fileManager.getGroup(args[1]);
if(group == null) {
messageBridge.message(sender, "groupdel-wrongGroup", args[1]);
return;
}
if(args.length == 2) {
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-weOnlyByPlayer");
return;
}
WorldEditSelection selection = worldEditInterface.getPlayerSelection(player);
if(selection == null) {
messageBridge.message(player, "cmd-noSelection");
return;
}
List<GeneralRegion> regions = Utils.getRegionsInSelection(selection);
if(regions.isEmpty()) {
messageBridge.message(player, "cmd-noRegionsFound");
return;
}
TreeSet<GeneralRegion> regionsSuccess = new TreeSet<>();
TreeSet<GeneralRegion> regionsFailed = new TreeSet<>();
for(GeneralRegion region : regions) {
if(group.removeMember(region)) {
regionsSuccess.add(region);
} else {
regionsFailed.add(region);
}
}
if(!regionsSuccess.isEmpty()) {
messageBridge.message(player, "groupdel-weSuccess", group.getName(), Utils.combinedMessage(regionsSuccess, "region"));
}
if(!regionsFailed.isEmpty()) {
messageBridge.message(player, "groupdel-weFailed", group.getName(), Utils.combinedMessage(regionsFailed, "region"));
}
// Update all regions, this does it in a task, updating them without lag
fileManager.updateRegions(regionsSuccess, player);
group.saveRequired();
} else {
GeneralRegion region = fileManager.getRegion(args[2]);
if(region == null) {
messageBridge.message(sender, "cmd-notRegistered", args[2]);
return;
}
if(group.removeMember(region)) {
region.update();
messageBridge.message(sender, "groupdel-success", group.getName(), group.getMembers().size(), region);
} else {
messageBridge.message(sender, "groupdel-failed", group.getName(), region);
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result = fileManager.getGroupNames();
} else if(toComplete == 3) {
result = fileManager.getRegionNames();
}
return result;
}
}
@@ -0,0 +1,79 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@Singleton
public class GroupinfoCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop groupinfo";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.groupinfo")) {
return "help-groupinfo";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.groupinfo")) {
messageBridge.message(sender, "groupinfo-noPermission");
return;
}
if(args.length < 2 || args[1] == null) {
messageBridge.message(sender, "groupinfo-help");
return;
}
RegionGroup group = fileManager.getGroup(args[1]);
if(group == null) {
messageBridge.message(sender, "groupinfo-noGroup", args[1]);
return;
}
Set<String> members = group.getMembers();
if(members.isEmpty()) {
messageBridge.message(sender, "groupinfo-noMembers", group.getName());
} else {
messageBridge.message(sender, "groupinfo-members", group.getName(), Utils.createCommaSeparatedList(members));
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result = fileManager.getGroupNames();
}
return result;
}
}
@@ -0,0 +1,63 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class GrouplistCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop grouplist";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.grouplist")) {
return "help-grouplist";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.grouplist")) {
messageBridge.message(sender, "grouplist-noPermission");
return;
}
List<String> groups = fileManager.getGroupNames();
if(groups.isEmpty()) {
messageBridge.message(sender, "grouplist-noGroups");
} else {
messageBridge.message(sender, "grouplist-success", Utils.createCommaSeparatedList(groups));
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
return new ArrayList<>();
}
}
@@ -0,0 +1,35 @@
package me.wiefferink.areashop.commands;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import me.wiefferink.areashop.managers.CommandManager;
import org.bukkit.command.CommandSender;
import javax.annotation.Nonnull;
public class HelpCommand extends CommandAreaShop {
private final CommandManager commandManager;
public HelpCommand(@Nonnull CommandManager commandManager) {
this.commandManager = commandManager;
}
@Override
public String getCommandStart() {
return "areashop help";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.help")) {
return "help-help";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
commandManager.showHelp(sender);
}
}
@@ -0,0 +1,88 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.regions.ImportJobFactory;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class ImportCommand extends CommandAreaShop {
/* RegionGroup priority usage:
0: Settings from /config.yml
1: Settings from /worlds/<world>/config.yml
2: Settings from /worlds/<world>/parent-regions.yml (if their priority is set it is added to this value)
*/
@Inject
private MessageBridge messageBridge;
@Inject
private ImportJobFactory importJobFactory;
@Override
public String getCommandStart() {
return "areashop import";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.import")) {
return "help-import";
}
return null;
}
// TODO:
// - Landlord?
// - Friends
// - Region flags?
// - Settings from the 'permissions' section in RegionForSale/config.yml?
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.import")) {
messageBridge.message(sender, "import-noPermission");
return;
}
if(args.length < 2) {
messageBridge.message(sender, "import-help");
return;
}
if(!"RegionForSale".equalsIgnoreCase(args[1])) {
messageBridge.message(sender, "import-wrongSource");
return;
}
if(!confirm(sender, args, Message.fromKey("import-confirm"))) {
return;
}
importJobFactory.createImportJob(sender).execute();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result.add("RegionForSale");
}
return result;
}
}
@@ -0,0 +1,512 @@
package me.wiefferink.areashop.commands;
import io.github.bakedlibs.dough.blocks.BlockPosition;
import io.github.md5sha256.areashop.CompositeMessage;
import io.github.md5sha256.areashop.KeyedMessage;
import io.github.md5sha256.areashop.MessageWrapper;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
// TODO fix '/as info' help message hitting replacement limit (improve depth tracking?)
// TODO add info about autoExtend
@Singleton
public class InfoCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop info";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.info")) {
return "help-info";
}
return null;
}
/**
* Display a page of a list of regions.
* @param sender The CommandSender to send the messages to
* @param regions The regions to display
* @param filterGroup The group to filter the regions by
* @param keyHeader The header to print above the page
* @param pageInput The page number, if any
* @param baseCommand The command to execute for next/previous page (/areashop will be added)
*/
private void showSortedPagedList(CommandSender sender, List<? extends GeneralRegion> regions, RegionGroup filterGroup, String keyHeader, String pageInput, String baseCommand) {
int maximumItems = 20;
int itemsPerPage = maximumItems - 2;
int page = 1;
if(pageInput != null && Utils.isNumeric(pageInput)) {
try {
page = Integer.parseInt(pageInput);
} catch(NumberFormatException e) {
messageBridge.message(sender, "info-wrongPage", pageInput);
return;
}
}
if(filterGroup != null) {
regions.removeIf(generalRegion -> !filterGroup.isMember(generalRegion));
}
if(regions.isEmpty()) {
messageBridge.message(sender, "info-noRegions");
} else {
// First sort by type, then by name
regions.sort((one, two) -> {
int typeCompare = getTypeOrder(two).compareTo(getTypeOrder(one));
if(typeCompare != 0) {
return typeCompare;
} else {
return one.getName().compareTo(two.getName());
}
});
// Header
if(filterGroup != null) {
KeyedMessage limitedToGroup = new KeyedMessage("info-limitedToGroup", filterGroup.getName());
messageBridge.message(sender, keyHeader, limitedToGroup);
} else {
messageBridge.message(sender, keyHeader);
}
// Page entries
int totalPages = (int)Math.ceil(regions.size() / (double)itemsPerPage); // Clip page to correct boundaries, not much need to tell the user
if(regions.size() == itemsPerPage + 1) { // 19 total items is mapped to 1 page of 19
itemsPerPage++;
totalPages = 1;
}
page = Math.max(1, Math.min(totalPages, page));
int linesPrinted = 1; // header
for(int i = (page - 1) * itemsPerPage; i < page * itemsPerPage && i < regions.size(); i++) {
String state;
GeneralRegion region = regions.get(i);
if(region.getType() == GeneralRegion.RegionType.RENT) {
if(region.getOwner() == null) {
state = "Forrent";
} else {
state = "Rented";
}
} else {
if(region.getOwner() == null) {
state = "Forsale";
} else if(!((BuyRegion)region).isInResellingMode()) {
state = "Sold";
} else {
state = "Reselling";
}
}
messageBridge.messageNoPrefix(sender, "info-entry" + state, region);
linesPrinted++;
}
CompositeMessage footer = new CompositeMessage();
// Previous button
MessageWrapper infoPage;
if(page > 1) {
infoPage = new KeyedMessage("info-pagePrevious",
baseCommand + " " + (page - 1));
} else {
infoPage = new KeyedMessage("info-pageNoPrevious");
}
footer.appendMessage(infoPage);
// Page status
if(totalPages > 1) {
StringBuilder pageString = new StringBuilder("" + page);
for(int i = pageString.length(); i < (totalPages + "").length(); i++) {
pageString.insert(0, "0");
}
MessageWrapper status = new KeyedMessage("info-pageStatus", page, totalPages);
footer.appendMessage(status);
MessageWrapper info;
if(page < totalPages) {
info = new KeyedMessage("info-pageNext", baseCommand + " " + (page + 1));
} else {
info = new KeyedMessage("info-pageNoNext");
}
footer.appendMessage(info);
// Fill up space if the page is not full (aligns header nicely)
String padding = " ".repeat(maximumItems - 1 - linesPrinted);
footer.appendText(padding);
}
messageBridge.message(sender, footer);
}
}
/**
* Get an integer to order by type, usable for Comparators.
* @param region The region to get the order for
* @return An integer for sorting by type
*/
private Integer getTypeOrder(GeneralRegion region) {
if(region.getType() == GeneralRegion.RegionType.RENT) {
if(region.getOwner() == null) {
return 1;
} else {
return 2;
}
} else {
if(region.getOwner() == null) {
return 3;
} else if(!((BuyRegion)region).isInResellingMode()) {
return 4;
} else {
return 5;
}
}
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.info")) {
messageBridge.message(sender, "info-noPermission");
return;
}
if(args.length > 1 && args[1] != null) {
// Get filter group (only used by some commands)
RegionGroup filterGroup = null;
Set<String> groupFilters = new HashSet<>(Arrays.asList("all", "rented", "forrent", "sold", "forsale", "reselling"));
if(groupFilters.contains(args[1].toLowerCase()) && args.length > 2) {
if(!Utils.isNumeric(args[2])) {
filterGroup = fileManager.getGroup(args[2]);
if(filterGroup == null) {
messageBridge.message(sender, "info-noFiltergroup", args[2]);
return;
}
// Pass page number to args[2] if available
if(args.length > 3) {
args[2] = args[3];
}
}
}
// All regions
if(args[1].equalsIgnoreCase("all")) {
showSortedPagedList(sender, new ArrayList<>(fileManager.getRegionsRef()), filterGroup, "info-allHeader", (args.length > 2 ? args[2] : null), "info all");
}
// Rented regions
else if(args[1].equalsIgnoreCase("rented")) {
List<RentRegion> regions = new ArrayList<>(fileManager.getRentsRef());
regions.removeIf(RentRegion::isAvailable);
showSortedPagedList(sender, regions, filterGroup, "info-rentedHeader", (args.length > 2 ? args[2] : null), "info rented");
}
// Forrent regions
else if(args[1].equalsIgnoreCase("forrent")) {
List<RentRegion> regions = new ArrayList<>(fileManager.getRentsRef());
regions.removeIf(RentRegion::isRented);
showSortedPagedList(sender, regions, filterGroup, "info-forrentHeader", (args.length > 2 ? args[2] : null), "info forrent");
}
// Sold regions
else if(args[1].equalsIgnoreCase("sold")) {
List<BuyRegion> regions = new ArrayList<>(fileManager.getBuysRef());
regions.removeIf(BuyRegion::isAvailable);
showSortedPagedList(sender, regions, filterGroup, "info-soldHeader", (args.length > 2 ? args[2] : null), "info sold");
}
// Forsale regions
else if(args[1].equalsIgnoreCase("forsale")) {
List<BuyRegion> regions = new ArrayList<>(fileManager.getBuysRef());
regions.removeIf(BuyRegion::isSold);
showSortedPagedList(sender, regions, filterGroup, "info-forsaleHeader", (args.length > 2 ? args[2] : null), "info forsale");
}
// Reselling regions
else if(args[1].equalsIgnoreCase("reselling")) {
List<BuyRegion> regions = new ArrayList<>(fileManager.getBuysRef());
regions.removeIf(region -> !region.isInResellingMode());
showSortedPagedList(sender, regions, filterGroup, "info-resellingHeader", (args.length > 2 ? args[2] : null), "info reselling");
}
// List of regions without a group
else if(args[1].equalsIgnoreCase("nogroup")) {
List<GeneralRegion> regions = new ArrayList<>(fileManager.getRegionsRef());
for(RegionGroup group : fileManager.getGroups()) {
regions.removeAll(group.getMemberRegions());
}
if(regions.isEmpty()) {
messageBridge.message(sender, "info-nogroupNone");
} else {
showSortedPagedList(sender, regions, filterGroup, "info-nogroupHeader", (args.length > 2 ? args[2] : null), "info nogroup");
}
}
// Region info
else if(args[1].equalsIgnoreCase("region")) {
if(args.length > 1) {
RentRegion rent = null;
BuyRegion buy = null;
if(args.length > 2) {
rent = fileManager.getRent(args[2]);
buy = fileManager.getBuy(args[2]);
} else {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
if(regions.get(0) instanceof RentRegion) {
rent = (RentRegion)regions.get(0);
} else if(regions.get(0) instanceof BuyRegion) {
buy = (BuyRegion)regions.get(0);
}
}
} else {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
if(rent == null && buy == null) {
messageBridge.message(sender, "info-regionNotExisting", args[2]);
return;
}
if(rent != null) {
messageBridge.message(sender, "info-regionHeaderRent", rent);
if(rent.isRented()) {
messageBridge.messageNoPrefix(sender, "info-regionRented", rent);
messageBridge.messageNoPrefix(sender, "info-regionExtending", rent);
// Money back
if(UnrentCommand.canUse(sender, rent)) {
messageBridge.messageNoPrefix(sender, "info-regionMoneyBackRentClick", rent);
} else {
messageBridge.messageNoPrefix(sender, "info-regionMoneyBackRent", rent);
}
// Friends
if(!rent.getFriendsFeature().getFriendNames().isEmpty()) {
String messagePart = "info-friend";
if(DelfriendCommand.canUse(sender, rent)) {
messagePart = "info-friendRemove";
}
messageBridge.messageNoPrefix(sender, "info-regionFriends", rent, Utils.combinedMessage(rent.getFriendsFeature().getFriendNames(), messagePart));
}
} else {
messageBridge.messageNoPrefix(sender, "info-regionCanBeRented", rent);
}
if(rent.getLandlordName() != null) {
messageBridge.messageNoPrefix(sender, "info-regionLandlord", rent);
}
// Maximum extends
if(rent.getMaxExtends() != -1) {
if(rent.getMaxExtends() == 0) {
messageBridge.messageNoPrefix(sender, "info-regionNoExtending", rent);
} else if(rent.isRented()) {
messageBridge.messageNoPrefix(sender, "info-regionExtendsLeft", rent);
} else {
messageBridge.messageNoPrefix(sender, "info-regionMaxExtends", rent);
}
}
// If maxExtends is zero it does not make sense to show this message
if(rent.getMaxRentTime() != -1 && rent.getMaxExtends() != 0) {
messageBridge.messageNoPrefix(sender, "info-regionMaxRentTime", rent);
}
if(rent.getInactiveTimeUntilUnrent() != -1) {
messageBridge.messageNoPrefix(sender, "info-regionInactiveUnrent", rent);
}
// Teleport
CompositeMessage tp = new CompositeMessage();
tp.appendMessage("info-prefix");
boolean foundSomething = false;
if(TeleportCommand.canUse(sender, rent)) {
foundSomething = true;
tp.appendMessage("info-regionTeleport", rent);
}
if(SetteleportCommand.canUse(sender, rent)) {
if(foundSomething) {
tp.appendText(", ");
}
foundSomething = true;
tp.appendMessage("info-setRegionTeleport", rent);
}
if(foundSomething) {
tp.appendText(".");
messageBridge.message(sender, tp);
}
// Signs
CompositeMessage signLocations = new CompositeMessage();
Iterator<BlockPosition> signPositions = rent.getSignsFeature().signManager().allSignLocations().iterator();
while (signPositions.hasNext()) {
BlockPosition location = signPositions.next();
MessageWrapper signInfo = new KeyedMessage("info-regionSignLocation", location.getWorld().getName(), location.getX(), location.getY(), location.getZ());
signLocations.appendMessage(signInfo);
if (signPositions.hasNext()) {
signLocations.appendText(", ");
}
}
messageBridge.message(sender, "info-regionSigns", signLocations);
// Groups
if(sender.hasPermission("areashop.groupinfo") && !rent.getGroupNames().isEmpty()) {
messageBridge.messageNoPrefix(sender, "info-regionGroups", Utils.createCommaSeparatedList(rent.getGroupNames()));
}
// Restoring
if(rent.isRestoreEnabled()) {
messageBridge.messageNoPrefix(sender, "info-regionRestoringRent", rent);
}
// Restrictions
if(!rent.isRented()) {
if(rent.restrictedToRegion()) {
messageBridge.messageNoPrefix(sender, "info-regionRestrictedRegionRent", rent);
} else if(rent.restrictedToWorld()) {
messageBridge.messageNoPrefix(sender, "info-regionRestrictedWorldRent", rent);
}
}
messageBridge.messageNoPrefix(sender, "info-regionFooterRent", rent);
} else if(buy != null) {
messageBridge.message(sender, "info-regionHeaderBuy", buy);
if(buy.isSold()) {
if(buy.isInResellingMode()) {
messageBridge.messageNoPrefix(sender, "info-regionReselling", buy);
messageBridge.messageNoPrefix(sender, "info-regionReselPrice", buy);
} else {
messageBridge.messageNoPrefix(sender, "info-regionBought", buy);
}
// Money back
if(!buy.getBooleanSetting("buy.sellDisabled")) {
if (SellCommand.canUse(sender, buy)) {
messageBridge.messageNoPrefix(sender, "info-regionMoneyBackBuyClick", buy);
} else {
messageBridge.messageNoPrefix(sender, "info-regionMoneyBackBuy", buy);
}
}
// Friends
if(!buy.getFriendsFeature().getFriendNames().isEmpty()) {
String messagePart = "info-friend";
if(DelfriendCommand.canUse(sender, buy)) {
messagePart = "info-friendRemove";
}
messageBridge.messageNoPrefix(sender, "info-regionFriends", buy, Utils.combinedMessage(buy.getFriendsFeature().getFriendNames(), messagePart));
}
} else {
messageBridge.messageNoPrefix(sender, "info-regionCanBeBought", buy);
}
if(buy.getLandlord() != null) {
messageBridge.messageNoPrefix(sender, "info-regionLandlord", buy);
}
if(buy.getInactiveTimeUntilSell() != -1) {
messageBridge.messageNoPrefix(sender, "info-regionInactiveSell", buy);
}
// Teleport
CompositeMessage tp = new CompositeMessage();
tp.appendMessage("info-prefix");
boolean foundSomething = false;
if(TeleportCommand.canUse(sender, buy)) {
foundSomething = true;
tp.appendMessage("info-regionTeleport",buy);
}
if(SetteleportCommand.canUse(sender, buy)) {
if(foundSomething) {
tp.appendText(", ");
}
foundSomething = true;
tp.appendMessage("info-setRegionTeleport", buy);
}
if(foundSomething) {
tp.appendText(".");
messageBridge.message(sender, tp);
}
// Signs
CompositeMessage signLocations = new CompositeMessage();
Iterator<BlockPosition> signPositions = buy.getSignsFeature().signManager().allSignLocations().iterator();
while (signPositions.hasNext()) {
BlockPosition location = signPositions.next();
signLocations.appendMessage("info-regionSignLocation", location.getWorld().getName(), location.getX(), location.getY(), location.getZ());
if (signPositions.hasNext()) {
signLocations.appendText(", ");
}
}
messageBridge.messageNoPrefix(sender, "info-regionSigns", signLocations);
// Groups
if(sender.hasPermission("areashop.groupinfo") && !buy.getGroupNames().isEmpty()) {
messageBridge.messageNoPrefix(sender, "info-regionGroups", Utils.createCommaSeparatedList(buy.getGroupNames()));
}
// Restoring
if(buy.isRestoreEnabled()) {
messageBridge.messageNoPrefix(sender, "info-regionRestoringBuy", buy);
}
// Restrictions
if(!buy.isSold()) {
if(buy.restrictedToRegion()) {
messageBridge.messageNoPrefix(sender, "info-regionRestrictedRegionBuy", buy);
} else if(buy.restrictedToWorld()) {
messageBridge.messageNoPrefix(sender, "info-regionRestrictedWorldBuy", buy);
}
}
messageBridge.messageNoPrefix(sender, "info-regionFooterBuy", buy);
}
} else {
messageBridge.message(sender, "info-regionHelp");
}
} else {
messageBridge.message(sender, "info-help");
}
} else {
messageBridge.message(sender, "info-help");
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result.addAll(Arrays.asList("all", "rented", "forrent", "sold", "forsale", "player", "region", "nogroup", "reselling"));
} else if(toComplete == 3) {
if(start[2].equalsIgnoreCase("player")) {
for(Player player : Utils.getOnlinePlayers()) {
result.add(player.getName());
}
} else if(start[2].equalsIgnoreCase("region")) {
result.addAll(fileManager.getBuyNames());
result.addAll(fileManager.getRentNames());
}
}
return result;
}
}
@@ -0,0 +1,99 @@
package me.wiefferink.areashop.commands;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.SignLinkerManager;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@Singleton
public class LinksignsCommand extends CommandAreaShop {
@Inject
private SignLinkerManager signLinkerManager;
@Inject
private AreaShop plugin;
@Inject
private MessageBridge messageBridge;
@Override
public String getCommandStart() {
return "areashop linksigns";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.linksigns")) {
return "help-linksigns";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.linksigns")) {
messageBridge.message(sender, "linksigns-noPermission");
return;
}
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-onlyByPlayer");
return;
}
if(signLinkerManager.isInSignLinkMode(player)) {
signLinkerManager.exitSignLinkMode(player);
} else {
// Get the profile
String profile = null;
if(args.length > 1) {
profile = args[1];
ConfigurationSection signProfilesSection = plugin.getConfig().getConfigurationSection("signProfiles");
if(signProfilesSection != null) {
Set<String> profiles = signProfilesSection.getKeys(false);
if(!profiles.contains(profile)) {
ArrayList<String> message = new ArrayList<>();
for(String p : profiles) {
if(!message.isEmpty()) {
message.add(", ");
}
message.addAll(Message.fromKey("addsign-profile").replacements(p).get());
}
messageBridge.message(sender, "addsign-wrongProfile", Message.fromList(message));
return;
}
}
}
signLinkerManager.enterSignLinkMode(player, profile);
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result.addAll(plugin.getFileManager().getRegionNames());
} else if(toComplete == 3) {
result.addAll(plugin.getConfig().getStringList("signProfiles"));
}
return result;
}
}
@@ -0,0 +1,161 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.adapters.platform.OfflinePlayerHelper;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.BukkitSchedulerExecutor;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Singleton
public class MeCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private OfflinePlayerHelper offlinePlayerHelper;
@Inject
private BukkitSchedulerExecutor executor;
@Override
public String getCommandStart() {
return "areashop me";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.me")) {
return "help-me";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.me")) {
messageBridge.message(sender, "me-noPermission");
return;
}
OfflinePlayer player = null;
if(!(sender instanceof Player)) {
if(args.length <= 1) {
messageBridge.message(sender, "me-notAPlayer");
return;
}
} else {
player = (OfflinePlayer)sender;
}
if(args.length > 1) {
// Lookup offline players async
this.offlinePlayerHelper.lookupOfflinePlayerAsync(args[1]).thenAcceptAsync(offlinePlayer -> {
if (!offlinePlayer.hasPlayedBefore()) {
messageBridge.message(sender, "me-noPlayer", args[1]);
} else {
sendMessageAboutRegions(sender, offlinePlayer);
}
}, this.executor);
return;
}
sendMessageAboutRegions(sender, player);
}
private void sendMessageAboutRegions(CommandSender sender, OfflinePlayer target) {
// Get the regions owned by the player
Set<RentRegion> rentRegions = new HashSet<>();
for(RentRegion region : fileManager.getRentsRef()) {
if(region.isOwner(target)) {
rentRegions.add(region);
}
}
Set<BuyRegion> buyRegions = new HashSet<>();
for(BuyRegion region : fileManager.getBuysRef()) {
if(region.isOwner(target)) {
buyRegions.add(region);
}
}
// Get the regions the player is added as friend
Set<GeneralRegion> friendRegions = new HashSet<>();
for(GeneralRegion region : fileManager.getRegionsRef()) {
if(region.getFriendsFeature().getFriends().contains(target.getUniqueId())) {
friendRegions.add(region);
}
}
// Send messages
boolean foundSome = !rentRegions.isEmpty() || !buyRegions.isEmpty() || !friendRegions.isEmpty();
if(!foundSome) {
messageBridge.message(sender, "me-nothing", target.getName());
return;
}
messageBridge.message(sender, "me-header", target.getName());
if(!rentRegions.isEmpty()) {
for(RentRegion region : rentRegions) {
messageBridge.messageNoPrefix(sender, "me-rentLine", region);
}
}
if(!buyRegions.isEmpty()) {
for(BuyRegion region : buyRegions) {
messageBridge.messageNoPrefix(sender, "me-buyLine", region);
}
}
if(!friendRegions.isEmpty()) {
for(GeneralRegion region : friendRegions) {
messageBridge.messageNoPrefix(sender, "me-friendLine", region);
}
}
messageBridge.messageNoPrefix(sender, "me-clickHint");
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(Player player : Utils.getOnlinePlayers()) {
result.add(player.getName());
}
}
return result;
}
}
@@ -0,0 +1,79 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class MessageCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Override
public String getCommandStart() {
return "areashop message";
}
@Override
public String getHelp(CommandSender target) {
// Internal command, no need to show in the help list
return null;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
if(!sender.hasPermission("areashop.message")) {
messageBridge.message(sender, "message-noPermission");
return;
}
if(args.length < 3) {
messageBridge.message(sender, "message-help");
return;
}
Player player = Bukkit.getPlayer(args[1]);
if(player == null) {
messageBridge.message(sender, "message-notOnline", args[1]);
return;
}
String[] messageArgs = new String[args.length - 2];
System.arraycopy(args, 2, messageArgs, 0, args.length - 2);
String message = StringUtils.join(messageArgs, " ");
Message.fromString(message).send(player);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
for(Player player : Utils.getOnlinePlayers()) {
result.add(player.getName());
}
}
return result;
}
}
@@ -0,0 +1,7 @@
# Commands
This module contains all AreaShop commands.
* `CommandAreaShop` is extended by all other classes, and implements some general functionality and defines which methods should be implemented.
* Other classes implement specific sub-commands, supply their help messages and tab completion.
* `CommandManager` class in the `managers` package handles incoming commands and dispatches them to the classes in this package.
@@ -0,0 +1,39 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import org.bukkit.command.CommandSender;
@Singleton
public class ReloadCommand extends CommandAreaShop {
@Inject
private AreaShop plugin;
@Inject
private MessageBridge messageBridge;
@Override
public String getCommandStart() {
return "areashop reload";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.reload")) {
return "help-reload";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(sender.hasPermission("areashop.reload")) {
// Reload the configuration files and update all region flags/signs
plugin.reload(sender);
} else {
messageBridge.message(sender, "reload-noPermission");
}
}
}
@@ -0,0 +1,78 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class RentCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop rent";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.rent")) {
return "help-rent";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.rent")) {
messageBridge.message(sender, "rent-noPermission");
return;
}
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-onlyByPlayer");
return;
}
if(args.length > 1 && args[1] != null) {
RentRegion rent = fileManager.getRent(args[1]);
if(rent == null) {
messageBridge.message(sender, "rent-notRentable", args[1]);
} else {
rent.rent(player);
}
} else {
// get the region by location
List<RentRegion> regions = Utils.getImportantRentRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
} else {
regions.get(0).rent(player);
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(RentRegion region : fileManager.getRentsRef()) {
if(!region.isRented()) {
result.add(region.getName());
}
}
}
return result;
}
}
@@ -0,0 +1,144 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class ResellCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop resell";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.resellall") || target.hasPermission("areashop.resell")) {
return "help-resell";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.resell") && !sender.hasPermission("areashop.resellall")) {
messageBridge.message(sender, "resell-noPermissionOther");
return;
}
if(args.length <= 1) {
messageBridge.message(sender, "resell-help");
return;
}
double price;
try {
price = Double.parseDouble(args[1]);
} catch(NumberFormatException e) {
messageBridge.message(sender, "resell-wrongPrice", args[1]);
return;
}
if(price < 0) {
messageBridge.message(sender, "resell-wrongPrice", args[1]);
return;
}
BuyRegion buy;
if(args.length <= 2) {
if(sender instanceof Player) {
// get the region by location
List<BuyRegion> regions = Utils.getImportantBuyRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
buy = regions.get(0);
}
} else {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
buy = fileManager.getBuy(args[2]);
if(buy == null) {
messageBridge.message(sender, "resell-notRegistered", args[2]);
return;
}
}
if(buy == null) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
}
if(!buy.isSold()) {
messageBridge.message(sender, "resell-notBought", buy);
return;
}
if(sender.hasPermission("areashop.resellall")) {
buy.enableReselling(price);
buy.update();
messageBridge.message(sender, "resell-success", buy);
} else if(sender.hasPermission("areashop.resell") && sender instanceof Player) {
if(!buy.isOwner((Player)sender)) {
messageBridge.message(sender, "resell-noPermissionOther", buy);
return;
}
if(buy.getBooleanSetting("buy.resellDisabled")) {
messageBridge.message(sender, "resell-disabled", buy);
return;
}
buy.enableReselling(price);
buy.update();
messageBridge.message(sender, "resell-success", buy);
} else {
messageBridge.message(sender, "resell-noPermission", buy);
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 3) {
for(BuyRegion region : fileManager.getBuysRef()) {
if(region.isSold() && !region.isInResellingMode()) {
result.add(region.getName());
}
}
}
return result;
}
}
@@ -0,0 +1,96 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Singleton
public class SchematiceventCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop schemevent";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.schematicevents")) {
return "help-schemevent";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.schematicevents")) {
messageBridge.message(sender, "schemevent-noPermission");
return;
}
if(args.length < 3 || args[1] == null || args[2] == null) {
messageBridge.message(sender, "schemevent-help");
return;
}
GeneralRegion region = fileManager.getRegion(args[1]);
if(region == null) {
messageBridge.message(sender, "cmd-notRegistered", args[1]);
return;
}
if(region.getRegion() == null) {
messageBridge.message(sender, "general-noRegion", region);
return;
}
GeneralRegion.RegionEvent event = null;
boolean exception = false;
try {
event = GeneralRegion.RegionEvent.valueOf(args[2].toUpperCase());
} catch(IllegalArgumentException e) {
exception = true;
}
// Check for a totally wrong event or a non matching event
if(exception) {
ArrayList<String> values = new ArrayList<>();
for(GeneralRegion.RegionEvent value : GeneralRegion.RegionEvent.values()) {
values.add(value.getValue().toLowerCase());
}
messageBridge.message(sender, "schemevent-wrongEvent", args[2], Utils.createCommaSeparatedList(values), region);
return;
}
region.handleSchematicEvent(event);
region.update();
messageBridge.message(sender, "schemevent-success", args[2], region);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
result.addAll(fileManager.getRegionNames());
} else if(toComplete == 3) {
GeneralRegion region = fileManager.getRegion(start[2]);
if(region != null) {
if(region instanceof RentRegion) {
result.addAll(Arrays.asList("created", "deleted", "rented", "unrented"));
} else if(region instanceof BuyRegion) {
result.addAll(Arrays.asList("created", "deleted", "bought", "sold"));
}
}
}
return result;
}
}
@@ -0,0 +1,119 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SellCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop sell";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.sell") || target.hasPermission("areashop.sellown")) {
return "help-sell";
}
return null;
}
/**
* Check if a person can sell the region.
* @param person The person to check
* @param region The region to check for
* @return true if the person can sell it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if(person.hasPermission("areashop.sell")) {
return true;
}
if(person instanceof Player player) {
return region.isOwner(player) && person.hasPermission("areashop.sellown");
}
return false;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.sell") && !sender.hasPermission("areashop.sellown")) {
messageBridge.message(sender, "sell-noPermission");
return;
}
BuyRegion buy;
if(args.length <= 1) {
if(sender instanceof Player player) {
// get the region by location
List<BuyRegion> regions = Utils.getImportantBuyRegions((player).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
buy = regions.get(0);
}
} else {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
buy = fileManager.getBuy(args[1]);
}
if(buy == null) {
messageBridge.message(sender, "sell-notRegistered");
return;
}
if(!buy.isSold()) {
messageBridge.message(sender, "sell-notBought", buy);
return;
}
buy.sell(true, sender);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(BuyRegion region : fileManager.getBuysRef()) {
if(region.isSold()) {
result.add(region.getName());
}
}
}
return result;
}
}
@@ -0,0 +1,118 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SetdurationCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private Plugin plugin;
@Override
public String getCommandStart() {
return "areashop setduration";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.setduration")) {
return "help-setduration";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.setduration") && (!sender.hasPermission("areashop.setduration.landlord") && sender instanceof Player)) {
messageBridge.message(sender, "setduration-noPermission");
return;
}
if(args.length < 3 || args[1] == null || args[2] == null) {
messageBridge.message(sender, "setduration-help");
return;
}
int regionArgument = 3;
if(args.length >= 2 && ("default".equalsIgnoreCase(args[1]) || "reset".equalsIgnoreCase(args[1]))) {
regionArgument = 2;
}
RentRegion rent;
if(args.length <= regionArgument) {
if(sender instanceof Player) {
// get the region by location
List<RentRegion> regions = Utils.getImportantRentRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
rent = regions.get(0);
}
} else {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
rent = fileManager.getRent(args[regionArgument]);
}
if(rent == null) {
messageBridge.message(sender, "setduration-notRegistered", args[regionArgument]);
return;
}
if(!sender.hasPermission("areashop.setduration") && !(sender instanceof Player && rent.isLandlord(((Player)sender).getUniqueId()))) {
messageBridge.message(sender, "setduration-noLandlord", rent);
return;
}
if("default".equalsIgnoreCase(args[1]) || "reset".equalsIgnoreCase(args[1])) {
rent.setDuration(null);
rent.update();
messageBridge.message(sender, "setduration-successRemoved", rent);
return;
}
try {
Integer.parseInt(args[1]);
} catch(NumberFormatException e) {
messageBridge.message(sender, "setduration-wrongAmount", args[1], rent);
return;
}
if(!Utils.checkTimeFormat(args[1] + " " + args[2])) {
messageBridge.message(sender, "setduration-wrongFormat", args[1] + " " + args[2], rent);
return;
}
rent.setDuration(args[1] + " " + args[2]);
rent.update();
messageBridge.message(sender, "setduration-success", rent);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 3) {
result.addAll(plugin.getConfig().getStringList("minutes"));
result.addAll(plugin.getConfig().getStringList("hours"));
result.addAll(plugin.getConfig().getStringList("days"));
result.addAll(plugin.getConfig().getStringList("months"));
result.addAll(plugin.getConfig().getStringList("years"));
} else if(toComplete == 4) {
result = fileManager.getRentNames();
}
return result;
}
}
@@ -0,0 +1,100 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.adapters.platform.OfflinePlayerHelper;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.BukkitSchedulerExecutor;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SetlandlordCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private OfflinePlayerHelper offlinePlayerHelper;
@Inject
private BukkitSchedulerExecutor executor;
@Override
public String getCommandStart() {
return "areashop setlandlord";
}
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.setlandlord")) {
return "help-setlandlord";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("areashop.setlandlord")) {
messageBridge.message(sender, "setlandlord-noPermission");
return;
}
if (args.length < 2) {
messageBridge.message(sender, "setlandlord-help");
return;
}
GeneralRegion region;
if (args.length < 3) {
if (!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(player.getLocation());
if (regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
region = regions.get(0);
}
} else {
region = fileManager.getRegion(args[2]);
}
if (region == null) {
messageBridge.message(sender, "cmd-notRegistered", args[2]);
return;
}
this.offlinePlayerHelper.lookupOfflinePlayerAsync(args[1]).thenAcceptAsync(offlinePlayer -> {
String playerName = offlinePlayer.getName();
if (playerName == null || playerName.isEmpty()) {
playerName = args[1];
}
region.setLandlord(offlinePlayer.getUniqueId(), playerName);
region.update();
messageBridge.message(sender, "setlandlord-success", playerName, region);
}, this.executor);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if (toComplete == 2) {
for (Player player : Utils.getOnlinePlayers()) {
result.add(player.getName());
}
} else if (toComplete == 3) {
result.addAll(fileManager.getRegionNames());
}
return result;
}
}
@@ -0,0 +1,142 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.adapters.platform.OfflinePlayerHelper;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.BukkitSchedulerExecutor;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;
@Singleton
public class SetownerCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private OfflinePlayerHelper offlinePlayerHelper;
@Inject
private BukkitSchedulerExecutor executor;
@Override
public String getCommandStart() {
return "areashop setowner";
}
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.setownerrent") || target.hasPermission("areashop.setownerbuy")) {
return "help-setowner";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("areashop.setownerrent") && !sender.hasPermission("areashop.setownerbuy")) {
messageBridge.message(sender, "setowner-noPermission");
return;
}
GeneralRegion region;
if (args.length < 2) {
messageBridge.message(sender, "setowner-help");
return;
}
if (args.length == 2) {
if (!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(player.getLocation());
if (regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
region = regions.get(0);
}
} else {
region = fileManager.getRegion(args[2]);
}
if (region == null) {
messageBridge.message(sender, "setowner-notRegistered");
return;
}
if (region instanceof RentRegion && !sender.hasPermission("areashop.setownerrent")) {
messageBridge.message(sender, "setowner-noPermissionRent", region);
return;
}
if (region instanceof BuyRegion && !sender.hasPermission("areashop.setownerbuy")) {
messageBridge.message(sender, "setowner-noPermissionBuy", region);
return;
}
this.offlinePlayerHelper.lookupUuidAsync(args[1]).thenAcceptAsync(optionalUuid -> {
if (optionalUuid.isEmpty()) {
messageBridge.message(sender, "setowner-noPlayer", args[1], region);
return;
}
final UUID uuid = optionalUuid.get();
if (region instanceof RentRegion rent) {
if (rent.isRenter(uuid)) {
// extend
rent.setRentedUntil(rent.getRentedUntil() + rent.getDuration());
rent.setRenter(uuid);
messageBridge.message(sender, "setowner-succesRentExtend", region);
} else {
// change
if (!rent.isRented()) {
rent.setRentedUntil(Calendar.getInstance().getTimeInMillis() + rent.getDuration());
}
rent.setRenter(uuid);
messageBridge.message(sender, "setowner-succesRent", region);
}
}
if (region instanceof BuyRegion buy) {
buy.setBuyer(uuid);
messageBridge.message(sender, "setowner-succesBuy", region);
}
region.getFriendsFeature().deleteFriend(region.getOwner(), null);
region.update();
region.saveRequired();
}, this.executor);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if (toComplete == 2) {
for (Player player : Utils.getOnlinePlayers()) {
result.add(player.getName());
}
} else if (toComplete == 3) {
result.addAll(fileManager.getRegionNames());
}
return result;
}
}
@@ -0,0 +1,113 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SetpriceCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop setprice";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.setprice")) {
return "help-setprice";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.setprice") && (!sender.hasPermission("areashop.setprice.landlord") && sender instanceof Player)) {
messageBridge.message(sender, "setprice-noPermission");
return;
}
if(args.length < 2 || args[1] == null) {
messageBridge.message(sender, "setprice-help");
return;
}
GeneralRegion region;
if(args.length < 3) {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
region = regions.get(0);
}
} else {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
region = fileManager.getRegion(args[2]);
}
if(region == null) {
messageBridge.message(sender, "setprice-notRegistered", args[2]);
return;
}
if(!sender.hasPermission("areashop.setprice") && !(sender instanceof Player && region.isLandlord(((Player)sender).getUniqueId()))) {
messageBridge.message(sender, "setprice-noLandlord", region);
return;
}
if("default".equalsIgnoreCase(args[1]) || "reset".equalsIgnoreCase(args[1])) {
if(region instanceof RentRegion) {
((RentRegion)region).setPrice(null);
} else if(region instanceof BuyRegion) {
((BuyRegion)region).setPrice(null);
}
region.update();
messageBridge.message(sender, "setprice-successRemoved", region);
return;
}
double price;
try {
price = Double.parseDouble(args[1]);
} catch(NumberFormatException e) {
messageBridge.message(sender, "setprice-wrongPrice", args[1], region);
return;
}
if(region instanceof RentRegion) {
((RentRegion)region).setPrice(price);
messageBridge.message(sender, "setprice-successRent", region);
} else if(region instanceof BuyRegion) {
((BuyRegion)region).setPrice(price);
messageBridge.message(sender, "setprice-successBuy", region);
}
region.update();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 3) {
result = fileManager.getRegionNames();
}
return result;
}
}
@@ -0,0 +1,90 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SetrestoreCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private Plugin plugin;
@Override
public String getCommandStart() {
return "areashop setrestore";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.setrestore")) {
return "help-setrestore";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.setrestore")) {
messageBridge.message(sender, "setrestore-noPermission");
return;
}
if(args.length <= 2 || args[1] == null || args[2] == null) {
messageBridge.message(sender, "setrestore-help");
return;
}
GeneralRegion region = fileManager.getRegion(args[1]);
if(region == null) {
messageBridge.message(sender, "setrestore-notRegistered", args[1]);
return;
}
Boolean value = null;
if(args[2].equalsIgnoreCase("true")) {
value = true;
} else if(args[2].equalsIgnoreCase("false")) {
value = false;
}
region.setRestoreSetting(value);
String valueString = "general";
if(value != null) {
valueString = value + "";
}
if(args.length > 3) {
region.setSchematicProfile(args[3]);
messageBridge.message(sender, "setrestore-successProfile", valueString, args[3], region);
} else {
messageBridge.message(sender, "setrestore-success", valueString, region);
}
region.update();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result = fileManager.getRegionNames();
} else if(toComplete == 3) {
result.add("true");
result.add("false");
result.add("general");
} else if(toComplete == 4) {
ConfigurationSection schemProfiles = fileManager.getConfig().getConfigurationSection("schematicProfiles");
if (schemProfiles != null) {
result.addAll(schemProfiles.getKeys(false));
}
}
return result;
}
}
@@ -0,0 +1,126 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SetteleportCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop settp";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.setteleportall") || target.hasPermission("areashop.setteleport")) {
return "help-setteleport";
}
return null;
}
/**
* Check if a person can set the teleport location of the region.
* @param person The person to check
* @param region The region to check for
* @return true if the person can set the teleport location, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if(!(person instanceof Player player)) {
return false;
}
return player.hasPermission("areashop.setteleportall")
|| region.isOwner(player) && player.hasPermission("areashop.setteleport");
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.setteleport") && !sender.hasPermission("areashop.setteleportall")) {
messageBridge.message(sender, "setteleport-noPermission");
return;
}
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "onlyByPlayer");
return;
}
GeneralRegion region;
if(args.length < 2) {
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
region = regions.get(0);
}
} else {
region = fileManager.getRegion(args[1]);
}
boolean owner;
if(region == null) {
messageBridge.message(player, "setteleport-noRentOrBuy", args[1]);
return;
}
if(region instanceof RentRegion) {
owner = player.getUniqueId().equals(((RentRegion)region).getRenter());
} else {
owner = player.getUniqueId().equals(((BuyRegion)region).getBuyer());
}
if(!player.hasPermission("areashop.setteleport")) {
messageBridge.message(player, "setteleport-noPermission", region);
return;
} else if(!owner && !player.hasPermission("areashop.setteleportall")) {
messageBridge.message(player, "setteleport-noPermissionOther", region);
return;
}
ProtectedRegion wgRegion = region.getRegion();
if(args.length > 2 && args[2] != null && (args[2].equalsIgnoreCase("reset") || args[2].equalsIgnoreCase("yes") || args[2].equalsIgnoreCase("true"))) {
region.getTeleportFeature().setTeleport(null);
region.update();
messageBridge.message(player, "setteleport-reset", region);
return;
}
if(!player.hasPermission("areashop.setteleportoutsideregion") && (wgRegion == null || !wgRegion.contains(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()))) {
messageBridge.message(player, "setteleport-notInside", region);
return;
}
region.getTeleportFeature().setTeleport(player.getLocation());
region.update();
messageBridge.message(player, "setteleport-success", region);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
result.addAll(fileManager.getRegionNames());
} else if(toComplete == 3) {
result.add("reset");
}
return result;
}
}
@@ -0,0 +1,282 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionFactory;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class StackCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Inject
private WorldEditInterface worldEditInterface;
@Inject
private WorldGuardInterface worldGuardInterface;
@Inject
private RegionFactory regionFactory;
@Inject
private Plugin plugin;
@Override
public String getCommandStart() {
return "areashop stack";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.stack")) {
return "help-stack";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
// Check permission
if(!sender.hasPermission("areashop.stack")) {
messageBridge.message(sender, "stack-noPermission");
return;
}
// Only from ingame
if(!(sender instanceof final Player player)) {
messageBridge.message(sender, "cmd-onlyByPlayer");
return;
}
// Specify enough arguments
if(args.length < 5) {
messageBridge.message(sender, "stack-help");
return;
}
// Check amount
int tempAmount = -1;
try {
tempAmount = Integer.parseInt(args[1]);
} catch(NumberFormatException e) {
// Incorrect number
}
if(tempAmount <= 0) {
messageBridge.message(player, "stack-wrongAmount", args[1]);
return;
}
// Check gap
int gap;
try {
gap = Integer.parseInt(args[2]);
} catch(NumberFormatException e) {
messageBridge.message(player, "stack-wrongGap", args[2]);
return;
}
// Check region type
if(!"rent".equalsIgnoreCase(args[4]) && !"buy".equalsIgnoreCase(args[4])) {
messageBridge.message(sender, "stack-help");
return;
}
// Get WorldEdit selection
final WorldEditSelection selection = worldEditInterface.getPlayerSelection(player);
if(selection == null) {
messageBridge.message(player, "stack-noSelection");
return;
}
// Get or create group
RegionGroup group = null;
if(args.length > 5) {
group = fileManager.getGroup(args[5]);
if(group == null) {
group = regionFactory.createRegionGroup(args[5]);
fileManager.addGroup(group);
}
}
// Get facing of the player (must be clearly one of the four directions to make sure it is no mistake)
BlockFace facing = Utils.yawToFacing(player.getLocation().getYaw());
if(player.getLocation().getPitch() > 45) {
facing = BlockFace.DOWN;
} else if(player.getLocation().getPitch() < -45) {
facing = BlockFace.UP;
}
if(!(facing == BlockFace.NORTH || facing == BlockFace.EAST || facing == BlockFace.SOUTH || facing == BlockFace.WEST || facing == BlockFace.UP || facing == BlockFace.DOWN)) {
messageBridge.message(player, "stack-unclearDirection", facing.toString().toLowerCase().replace('_', '-'));
return;
}
Vector shift = new Vector(0, 0, 0);
if(facing == BlockFace.SOUTH) {
shift = shift.setZ(-selection.getLength() - gap);
} else if(facing == BlockFace.WEST) {
shift = shift.setX(selection.getWidth() + gap);
} else if(facing == BlockFace.NORTH) {
shift = shift.setZ(selection.getLength() + gap);
} else if(facing == BlockFace.EAST) {
shift = shift.setX(-selection.getWidth() - gap);
} else if(facing == BlockFace.DOWN) {
shift = shift.setY(-selection.getHeight() - gap);
} else if(facing == BlockFace.UP) {
shift = shift.setY(selection.getHeight() + gap);
}
AreaShop.debug(" calculated shift vector: " + shift + ", with facing=" + facing);
// Create regions and add them to AreaShop
final String nameTemplate = args[3];
final int regionsPerTick = plugin.getConfig().getInt("adding.regionsPerTick");
final boolean rentRegions = "rent".equalsIgnoreCase(args[4]);
final int amount = tempAmount;
final RegionGroup finalGroup = group;
final Vector finalShift = shift;
String type;
if(rentRegions) {
type = "rent";
} else {
type = "buy";
}
Message groupsMessage = Message.empty();
if(group != null) {
groupsMessage = Message.fromKey("stack-addToGroup").replacements(group.getName());
}
messageBridge.message(player, "stack-accepted", amount, type, gap, nameTemplate, groupsMessage);
messageBridge.message(player, "stack-addStart", amount, regionsPerTick * 20);
Location minimumLocation = selection.getMinimumLocation();
Vector minimumVector = new Vector(minimumLocation.getX(), minimumLocation.getY(), minimumLocation.getZ());
Location maximumLocation = selection.getMaximumLocation();
Vector maximumVector = new Vector(maximumLocation.getX(), maximumLocation.getY(), maximumLocation.getZ());
new BukkitRunnable() {
private int current = -1;
private final RegionManager manager = AreaShop.getInstance().getRegionManager(selection.getWorld());
private int counter = 1;
private int tooLow = 0;
private int tooHigh = 0;
@Override
public void run() {
for(int i = 0; i < regionsPerTick; i++) {
current++;
if(current < amount) {
// Create the region name
String regionName = countToName(nameTemplate, counter);
while(manager.getRegion(regionName) != null || AreaShop.getInstance().getFileManager().getRegion(regionName) != null) {
counter++;
regionName = countToName(nameTemplate, counter);
}
// Add the region to WorldGuard (at startposition shifted by the number of this region times the blocks it should shift)
Vector minimum = minimumVector.clone().add(finalShift.clone().multiply(current));
Vector maximum = maximumVector.clone().add(finalShift.clone().multiply(current));
// Check for out of bounds
if(minimum.getBlockY() < 0) {
tooLow++;
continue;
} else if(maximum.getBlockY() > 256) {
tooHigh++;
continue;
}
ProtectedCuboidRegion region = worldGuardInterface.createCuboidRegion(regionName, minimum,maximum);
manager.addRegion(region);
// Add the region to AreaShop
GeneralRegion newRegion;
if(rentRegions) {
newRegion = regionFactory.createRentRegion(regionName, selection.getWorld());
} else {
newRegion = regionFactory.createBuyRegion(regionName, selection.getWorld());
}
if(finalGroup != null) {
finalGroup.addMember(newRegion);
}
AddingRegionEvent event = fileManager.addRegion(newRegion);
if (event.isCancelled()) {
messageBridge.message(player, "general-cancelled", event.getReason());
continue;
}
newRegion.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
newRegion.update();
}
}
if(current >= amount) {
if(player.isOnline()) {
int added = amount - tooLow - tooHigh;
Message wrong = Message.empty();
if(tooHigh > 0) {
wrong.append(Message.fromKey("stack-tooHigh").replacements(tooHigh));
}
if(tooLow > 0) {
wrong.append(Message.fromKey("stack-tooLow").replacements(tooLow));
}
messageBridge.message(player, "stack-addComplete", added, wrong);
}
this.cancel();
}
}
}.runTaskTimer(plugin, 1, 1);
}
/**
* Build a name from a count, with the right length.
* @param template Template to put the name in (# to put the count there, otherwise count is appended)
* @param count Number to use
* @return name with prepended 0's
*/
private String countToName(String template, int count) {
StringBuilder counterName = new StringBuilder().append(count);
int minimumLength = plugin.getConfig().getInt("stackRegionNumberLength");
while(counterName.length() < minimumLength) {
counterName.insert(0, "0");
}
if(template.contains("#")) {
return template.replace("#", counterName);
} else {
return template + counterName;
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 5) {
result.add("rent");
result.add("buy");
} else if(toComplete == 6) {
result.addAll(fileManager.getGroupNames());
}
return result;
}
}
@@ -0,0 +1,121 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class StopresellCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop stopresell";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.stopresellall") || target.hasPermission("areashop.stopresell")) {
return "help-stopResell";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.stopresell") && !sender.hasPermission("areashop.stopresellall")) {
messageBridge.message(sender, "stopresell-noPermissionOther");
return;
}
BuyRegion buy;
if(args.length <= 1) {
if(sender instanceof Player) {
// get the region by location
List<BuyRegion> regions = Utils.getImportantBuyRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
buy = regions.get(0);
}
} else {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
buy = fileManager.getBuy(args[1]);
if(buy == null) {
messageBridge.message(sender, "stopresell-notRegistered", args[1]);
return;
}
}
if(buy == null) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
}
if(!buy.isInResellingMode()) {
messageBridge.message(sender, "stopresell-notResell", buy);
return;
}
if(sender.hasPermission("areashop.stopresellall")) {
buy.disableReselling();
buy.update();
messageBridge.message(sender, "stopresell-success", buy);
} else if(sender.hasPermission("areashop.stopresell") && sender instanceof Player) {
if(buy.isOwner((Player)sender)) {
buy.disableReselling();
buy.update();
messageBridge.message(sender, "stopresell-success", buy);
} else {
messageBridge.message(sender, "stopresell-noPermissionOther", buy);
}
} else {
messageBridge.message(sender, "stopresell-noPermission", buy);
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(BuyRegion region : fileManager.getBuysRef()) {
if(region.isSold() && region.isInResellingMode()) {
result.add(region.getName());
}
}
}
return result;
}
}
@@ -0,0 +1,90 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class TeleportCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop tp";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.teleportall") || target.hasPermission("areashop.teleport")) {
return "help-teleport";
}
return null;
}
/**
* Check if a person can teleport to the region (assuming he is not teleporting to a sign).
* @param person The person to check
* @param region The region to check for
* @return true if the person can teleport to it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if(!(person instanceof Player player)) {
return false;
}
return player.hasPermission("areashop.teleportall")
|| region.isOwner(player) && player.hasPermission("areashop.teleport")
|| region.isAvailable() && player.hasPermission("areashop.teleportavailable")
|| region.getFriendsFeature().getFriends().contains(player.getUniqueId()) && player.hasPermission("areashop.teleportfriend");
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.teleport") && !sender.hasPermission("areashop.teleportall") && !sender.hasPermission("areashop.teleportavailable") && !sender.hasPermission("areashop.teleportavailablesign") && !sender.hasPermission("areashop.teleportsign") && !sender.hasPermission("areashop.teleportsignall") && !sender.hasPermission("areashop.teleportfriend") && !sender.hasPermission("teleportfriendsign")) {
messageBridge.message(sender, "teleport-noPermission");
return;
}
if(!(sender instanceof Player player)) {
messageBridge.message(sender, "cmd-onlyByPlayer");
return;
}
if(args.length <= 1 || args[1] == null) {
messageBridge.message(sender, "teleport-help");
return;
}
GeneralRegion region = fileManager.getRegion(args[1]);
if(region == null) {
messageBridge.message(player, "teleport-noRentOrBuy", args[1]);
return;
}
boolean toSign = args.length >= 3 && (
args[2].equalsIgnoreCase("sign")
|| args[2].equalsIgnoreCase("yes")
|| args[2].equalsIgnoreCase("true")
);
region.getTeleportFeature().teleportPlayer(player, toSign);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
result.addAll(fileManager.getRegionNames());
} else if(toComplete == 3) {
result.add("sign");
}
return result;
}
}
@@ -0,0 +1,111 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class UnrentCommand extends CommandAreaShop {
@Inject
private MessageBridge messageBridge;
@Inject
private IFileManager fileManager;
@Override
public String getCommandStart() {
return "areashop unrent";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.unrent") || target.hasPermission("areashop.unrentown")) {
return "help-unrent";
}
return null;
}
/**
* Check if a person can unrent the region.
* @param person The person to check
* @param region The region to check for
* @return true if the person can unrent it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if(person.hasPermission("areashop.unrent")) {
return true;
}
if(person instanceof Player player) {
return region.isOwner(player) && person.hasPermission("areashop.unrentown");
}
return false;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.unrent") && !sender.hasPermission("areashop.unrentown")) {
messageBridge.message(sender, "unrent-noPermission");
return;
}
RentRegion rent;
if(args.length <= 1) {
if(sender instanceof Player) {
// get the region by location
List<RentRegion> regions = Utils.getImportantRentRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
messageBridge.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
messageBridge.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
rent = regions.get(0);
}
} else {
messageBridge.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
rent = fileManager.getRent(args[1]);
}
if(rent == null) {
messageBridge.message(sender, "unrent-notRegistered");
return;
}
if(!rent.isRented()) {
messageBridge.message(sender, "unrent-notRented", rent);
return;
}
rent.unRent(true, sender);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(RentRegion region : fileManager.getRentsRef()) {
if(region.isRented()) {
result.add(region.getName());
}
}
}
return result;
}
}
@@ -0,0 +1,45 @@
package me.wiefferink.areashop.events;
public class CancellableRegionEvent<T> extends RegionEvent<T> {
private boolean cancelled;
private String reason;
public CancellableRegionEvent(T region) {
super(region);
}
/**
* Cancel the event from happening.
* @param reason The reason of cancelling, used for display to the user, should end with a dot
*/
public void cancel(String reason) {
this.cancelled = true;
this.reason = reason;
}
/**
* Let the event continue, possible overwriting a cancel() call from another plugin.
*/
public void allow() {
this.cancelled = false;
this.reason = null;
}
/**
* Check if the event has been cancelled.
* @return true if the event has been cancelled, otherwise false
*/
public boolean isCancelled() {
return cancelled;
}
/**
* Get the reason why this event is cancelled.
* @return null if there is no reason or the event is not cancelled, otherwise a string
*/
public String getReason() {
return reason;
}
}
@@ -0,0 +1,7 @@
package me.wiefferink.areashop.events;
public class NotifyRegionEvent<T> extends RegionEvent<T> {
public NotifyRegionEvent(T region) {
super(region);
}
}
@@ -0,0 +1,7 @@
# Events
This package contains event classes that allow other plugins to listen to events about AreaShop regions.
* `RegionEvent` class represents a generic event.
* `CancellableRegionEvent` represents an event that has a region and can be cancelled.
* `NotifyRegionEvent` represents events that are to inform only, and cannot be cancelled anymore (an `CancellableRegionEvent` would be sent first).
@@ -0,0 +1,31 @@
package me.wiefferink.areashop.events;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class RegionEvent<T> extends Event {
protected T region;
private static final HandlerList handlers = new HandlerList();
public RegionEvent(T region) {
this.region = region;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
// Required by Bukkit/Spigot
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Get the region of this event.
* @return The region the event is about
*/
public T getRegion() {
return region;
}
}
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been added to AreaShop.
*/
public class AddingRegionEvent extends CancellableRegionEvent<GeneralRegion> {
/**
* Constructor.
* @param region The region that has been added
*/
public AddingRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -0,0 +1,31 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import org.bukkit.OfflinePlayer;
/**
* Broadcasted when a player tries to buy a region.
*/
public class BuyingRegionEvent extends CancellableRegionEvent<BuyRegion> {
private final OfflinePlayer player;
/**
* Constructor.
* @param region The region that is about to get bought
* @param player The player that tries to buy the region
*/
public BuyingRegionEvent(BuyRegion region, OfflinePlayer player) {
super(region);
this.player = player;
}
/**
* Get the player that is trying to buy the region.
* @return The player that is trying to buy the region
*/
public OfflinePlayer getPlayer() {
return player;
}
}
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been added to AreaShop.
*/
public class DeletingRegionEvent extends CancellableRegionEvent<GeneralRegion> {
/**
* Constructor.
* @param region The region that has been added
*/
public DeletingRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -0,0 +1,42 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.RentRegion;
import org.bukkit.OfflinePlayer;
/**
* Broadcasted when a player tries to rent a region.
*/
public class RentingRegionEvent extends CancellableRegionEvent<RentRegion> {
private final OfflinePlayer player;
private final boolean extending;
/**
* Constructor.
* @param region The region that is about to be rented
* @param player The player that tries to rent the region
* @param extending true if the player is extending the rental of the region, otherwise false
*/
public RentingRegionEvent(RentRegion region, OfflinePlayer player, boolean extending) {
super(region);
this.player = player;
this.extending = extending;
}
/**
* Get the player that is trying to rent the region.
* @return The player that is trying to rent the region
*/
public OfflinePlayer getPlayer() {
return player;
}
/**
* Check if the player is extending the region or renting it for the first time.
* @return true if the player tries to extend the region, false if he tries to rent it the first time
*/
public boolean isExtending() {
return extending;
}
}
@@ -0,0 +1,31 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import org.bukkit.OfflinePlayer;
/**
* Broadcasted when a player tries to resell a region.
*/
public class ResellingRegionEvent extends CancellableRegionEvent<BuyRegion> {
private final OfflinePlayer player;
/**
* Contructor.
* @param region The region that the player is trying to resell
* @param player The player that is trying to buy this region from the current owner
*/
public ResellingRegionEvent(BuyRegion region, OfflinePlayer player) {
super(region);
this.player = player;
}
/**
* Get the player that is trying to buy the region.
* @return The player that is trying to buy the region
*/
public OfflinePlayer getBuyer() {
return player;
}
}
@@ -0,0 +1,18 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
/**
* Broadcasted when a region is about to get sold.
*/
public class SellingRegionEvent extends CancellableRegionEvent<BuyRegion> {
/**
* Constructor.
* @param region The region that is about to get sold
*/
public SellingRegionEvent(BuyRegion region) {
super(region);
}
}
@@ -0,0 +1,18 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.RentRegion;
/**
* Broadcasted when a region is about to be unrented.
*/
public class UnrentingRegionEvent extends CancellableRegionEvent<RentRegion> {
/**
* Constructor.
* @param region The region that is about to be unrented
*/
public UnrentingRegionEvent(RentRegion region) {
super(region);
}
}
@@ -0,0 +1,43 @@
package me.wiefferink.areashop.events.askandnotify;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
/**
* Broadcasted when a friend is being added to a region.
*/
public class AddedFriendEvent extends CancellableRegionEvent<GeneralRegion> {
private final OfflinePlayer friend;
private final CommandSender by;
/**
* Constructor.
* @param region The region the friend is getting added to
* @param friend The friend that is about to be added
* @param by The CommandSender that is adding the friend, or null if none
*/
public AddedFriendEvent(GeneralRegion region, OfflinePlayer friend, CommandSender by) {
super(region);
this.friend = friend;
this.by = by;
}
/**
* Get the OfflinePlayer that is getting added as friend.
* @return The friend that is getting added
*/
public OfflinePlayer getFriend() {
return friend;
}
/**
* Get the CommandSender that is adding the friend.
* @return null if none, a CommandSender if done by someone (likely Player or ConsoleCommandSender)
*/
public CommandSender getBy() {
return by;
}
}
@@ -0,0 +1,43 @@
package me.wiefferink.areashop.events.askandnotify;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
/**
* Broadcasted when a friend is being added to a region.
*/
public class DeletedFriendEvent extends CancellableRegionEvent<GeneralRegion> {
private final OfflinePlayer friend;
private final CommandSender by;
/**
* Constructor.
* @param region The region the friend is getting added to
* @param friend The friend that is about to be added
* @param by The CommandSender that is adding the friend, or null if none
*/
public DeletedFriendEvent(GeneralRegion region, OfflinePlayer friend, CommandSender by) {
super(region);
this.friend = friend;
this.by = by;
}
/**
* Get the OfflinePlayer that is getting added as friend.
* @return The friend that is getting added
*/
public OfflinePlayer getFriend() {
return friend;
}
/**
* Get the CommandSender that is adding the friend.
* @return null if none, a CommandSender if done by someone (likely Player or ConsoleCommandSender)
*/
public CommandSender getBy() {
return by;
}
}
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been added to AreaShop.
*/
public class AddedRegionEvent extends NotifyRegionEvent<GeneralRegion> {
/**
* Constructor.
* @param region The region that has been added
*/
public AddedRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
/**
* Broadcasted when a region has been bought.
*/
public class BoughtRegionEvent extends NotifyRegionEvent<BuyRegion> {
/**
* Constructor.
* @param region The region that has been bought
*/
public BoughtRegionEvent(BuyRegion region) {
super(region);
}
}
@@ -0,0 +1,18 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been removed from AreaShop.
*/
public class DeletedRegionEvent extends NotifyRegionEvent<GeneralRegion> {
/**
* Constructor.
* @param region The region that has been removed
*/
public DeletedRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -0,0 +1,31 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.RentRegion;
/**
* Broadcasted when a region has been rented.
*/
public class RentedRegionEvent extends NotifyRegionEvent<RentRegion> {
private final boolean extended;
/**
* Constructor.
* @param region The region that has been rented
* @param extended true if the region has been extended, false if this is the first time buying the region
*/
public RentedRegionEvent(RentRegion region, boolean extended) {
super(region);
this.region = region;
this.extended = extended;
}
/**
* Check if the region was extended or rented for the first time.
* @return true if the region was extended, false when rented for the first time
*/
public boolean hasExtended() {
return extended;
}
}
@@ -0,0 +1,32 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import java.util.UUID;
/**
* Broadcasted when a region has been resold.
*/
public class ResoldRegionEvent extends NotifyRegionEvent<BuyRegion> {
private final UUID from;
/**
* Constructor.
* @param region The region that has been resold
* @param from The player from who the region has been resold to the current owner
*/
public ResoldRegionEvent(BuyRegion region, UUID from) {
super(region);
this.from = from;
}
/**
* Get the player that the region has been bought from.
* @return The UUID of the player that the region has been bought from
*/
public UUID getFromPlayer() {
return from;
}
}
@@ -0,0 +1,43 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import java.util.UUID;
/**
* Broadcasted when a region is sold.
*/
public class SoldRegionEvent extends NotifyRegionEvent<BuyRegion> {
private final UUID oldBuyer;
private final double refundedMoney;
/**
* Constructor.
* @param region The region that has been sold
* @param oldBuyer The player for which the region has been sold
* @param refundedMoney The amount of money that has been refunded
*/
public SoldRegionEvent(BuyRegion region, UUID oldBuyer, double refundedMoney) {
super(region);
this.oldBuyer = oldBuyer;
this.refundedMoney = refundedMoney;
}
/**
* Get the player that the region is sold for.
* @return The UUID of the player that the region is sold for
*/
public UUID getOldBuyer() {
return oldBuyer;
}
/**
* Get the amount that is paid back to the player.
* @return The amount of money paid back to the player
*/
public double getRefundedMoney() {
return refundedMoney;
}
}
@@ -0,0 +1,43 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.RentRegion;
import java.util.UUID;
/**
* Broadcasted when a region has been unrented.
*/
public class UnrentedRegionEvent extends NotifyRegionEvent<RentRegion> {
private final UUID oldRenter;
private final double refundedMoney;
/**
* Constructor.
* @param region The region that has been unrented
* @param oldRenter The player that rented the region before it was unrented
* @param refundedMoney The amount of money that has been refunded
*/
public UnrentedRegionEvent(RentRegion region, UUID oldRenter, double refundedMoney) {
super(region);
this.oldRenter = oldRenter;
this.refundedMoney = refundedMoney;
}
/**
* Get the player that the region was unrented for.
* @return The UUID of the player that the region was unrented for
*/
public UUID getOldRenter() {
return oldRenter;
}
/**
* Get the amount that is paid back to the player.
* @return The amount of money paid back to the player
*/
public double getRefundedMoney() {
return refundedMoney;
}
}
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when the data of a region changes.
* Should be used for updating displays that use region data.
*/
public class UpdateRegionEvent extends NotifyRegionEvent<GeneralRegion> {
/**
* Contructor.
* @param region The region that has been updated
*/
public UpdateRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -0,0 +1,127 @@
package me.wiefferink.areashop.features;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.events.ask.BuyingRegionEvent;
import me.wiefferink.areashop.events.ask.DeletingRegionEvent;
import me.wiefferink.areashop.events.ask.RentingRegionEvent;
import me.wiefferink.areashop.events.ask.ResellingRegionEvent;
import me.wiefferink.areashop.events.ask.SellingRegionEvent;
import me.wiefferink.areashop.events.ask.UnrentingRegionEvent;
import me.wiefferink.areashop.events.notify.AddedRegionEvent;
import me.wiefferink.areashop.events.notify.BoughtRegionEvent;
import me.wiefferink.areashop.events.notify.DeletedRegionEvent;
import me.wiefferink.areashop.events.notify.RentedRegionEvent;
import me.wiefferink.areashop.events.notify.ResoldRegionEvent;
import me.wiefferink.areashop.events.notify.SoldRegionEvent;
import me.wiefferink.areashop.events.notify.UnrentedRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import java.util.List;
@Singleton
public class CommandsFeature extends RegionFeature {
@Inject
CommandsFeature(AreaShop plugin) {
super(plugin);
}
/**
* Run command for a certain event.
* @param region Region to execute the events for
* @param event The event
* @param before The 'before' or 'after' commands
*/
public void runEventCommands(GeneralRegion region, GeneralRegion.RegionEvent event, boolean before) {
ConfigurationSection eventCommandProfileSection = region.getConfigurationSectionSetting("general.eventCommandProfile", "eventCommandProfiles");
if(eventCommandProfileSection == null) {
return;
}
List<String> commands = eventCommandProfileSection.getStringList(event.getValue() + "." + (before ? "before" : "after"));
if(commands.isEmpty()) {
return;
}
region.runCommands(Bukkit.getConsoleSender(), commands);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void addingRegion(AddingRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.CREATED, true);
}
@EventHandler
public void addedRegion(AddedRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.CREATED, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void deletingRegion(DeletingRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.DELETED, true);
}
@EventHandler
public void deletedRegion(DeletedRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.DELETED, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void rentingRegion(RentingRegionEvent event) {
// Technically the rent can still be cancelled if the payment fails...
runEventCommands(event.getRegion(), event.isExtending() ? GeneralRegion.RegionEvent.EXTENDED : GeneralRegion.RegionEvent.RENTED, true);
}
@EventHandler
public void rentedRegion(RentedRegionEvent event) {
runEventCommands(event.getRegion(), event.hasExtended() ? GeneralRegion.RegionEvent.EXTENDED : GeneralRegion.RegionEvent.RENTED, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void buyingRegion(BuyingRegionEvent event) {
// Technically the buy can still be cancelled if the payment fails...
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.BOUGHT, true);
}
@EventHandler
public void boughtRegion(BoughtRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.BOUGHT, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void resellingRegion(ResellingRegionEvent event) {
// Technically the resell can still be cancelled if the payment fails...
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.RESELL, true);
}
@EventHandler
public void resoldRegion(ResoldRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.RESELL, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void sellingRegion(SellingRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.SOLD, true);
}
@EventHandler
public void soldRegion(SoldRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.SOLD, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void unrentingRegion(UnrentingRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.UNRENTED, true);
}
@EventHandler
public void unrentedRegion(UnrentedRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.UNRENTED, false);
}
}
@@ -0,0 +1,108 @@
package me.wiefferink.areashop.features;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.events.ask.BuyingRegionEvent;
import me.wiefferink.areashop.events.ask.RentingRegionEvent;
import me.wiefferink.areashop.events.ask.ResellingRegionEvent;
import me.wiefferink.areashop.events.ask.SellingRegionEvent;
import me.wiefferink.areashop.events.ask.UnrentingRegionEvent;
import me.wiefferink.areashop.events.askandnotify.AddedFriendEvent;
import me.wiefferink.areashop.events.askandnotify.DeletedFriendEvent;
import me.wiefferink.areashop.events.notify.AddedRegionEvent;
import me.wiefferink.areashop.events.notify.BoughtRegionEvent;
import me.wiefferink.areashop.events.notify.DeletedRegionEvent;
import me.wiefferink.areashop.events.notify.RentedRegionEvent;
import me.wiefferink.areashop.events.notify.ResoldRegionEvent;
import me.wiefferink.areashop.events.notify.SoldRegionEvent;
import me.wiefferink.areashop.events.notify.UnrentedRegionEvent;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.event.EventHandler;
import javax.annotation.Nonnull;
@Singleton
public class DebugFeature extends RegionFeature {
@Inject
DebugFeature(@Nonnull AreaShop plugin) {
super(plugin);
}
@EventHandler
public void buyingRegion(BuyingRegionEvent event) {
AreaShop.debug("BuyingRegionEvent: " + event.getPlayer().getName() + " is trying to buy " + event.getRegion().getName());
}
@EventHandler
public void boughtRegion(BoughtRegionEvent event) {
AreaShop.debug("BoughtRegionEvent: " + event.getRegion().getPlayerName() + " bought " + event.getRegion().getName());
}
@EventHandler
public void sellingRegion(SellingRegionEvent event) {
AreaShop.debug("SellingRegionEvent: " + event.getRegion().getName() + " is trying to sell " + event.getRegion().getName());
}
@EventHandler
public void soldRegion(SoldRegionEvent event) {
AreaShop.debug("SoldRegionEvent: " + Utils.toName(event.getOldBuyer()) + " sold " + event.getRegion().getName());
}
@EventHandler
public void resellingRegion(ResellingRegionEvent event) {
AreaShop.debug("ResellingRegionEvent: " + event.getRegion().getName() + " is trying to resell " + event.getRegion().getName());
}
@EventHandler
public void resoldRegion(ResoldRegionEvent event) {
AreaShop.debug("ResoldRegionEvent: " + Utils.toName(event.getFromPlayer()) + " resold " + event.getRegion().getName() + " to " + event.getRegion().getPlayerName());
}
@EventHandler
public void rentingRegion(RentingRegionEvent event) {
AreaShop.debug("RentingRegionEvent: " + event.getPlayer().getName() + " is trying to rent " + event.getRegion().getName() + ", extending=" + event.isExtending());
}
@EventHandler
public void rentedRegion(RentedRegionEvent event) {
AreaShop.debug("RentedRegionEvent: " + event.getRegion().getPlayerName() + " rented " + event.getRegion().getName() + ", extending=" + event.hasExtended());
}
@EventHandler
public void unrentingRegion(UnrentingRegionEvent event) {
AreaShop.debug("UnrentingRegionEvent: " + event.getRegion().getPlayerName() + " is trying to unrent " + event.getRegion().getName());
}
@EventHandler
public void unrentedRegion(UnrentedRegionEvent event) {
AreaShop.debug("UnrentedRegionEvent: " + Utils.toName(event.getOldRenter()) + " unrented " + event.getRegion().getName());
}
@EventHandler
public void regionUpdate(UpdateRegionEvent event) {
//AreaShop.debug("UpdateRegionEvent: " + event.getRegion().getName() + " updated");
}
@EventHandler
public void addedRegion(AddedRegionEvent event) {
AreaShop.debug("AddedRegionEvent: " + event.getRegion().getName());
}
@EventHandler
public void removedRegion(DeletedRegionEvent event) {
AreaShop.debug("DeletedRegionEvent: " + event.getRegion().getName());
}
@EventHandler
public void addedFriend(AddedFriendEvent event) {
AreaShop.debug("AddedFriendEvent: region " + event.getRegion().getName() + ", " + event.getFriend().getName() + " by " + (event.getBy() == null ? "<nobody>" : event.getBy().getName()));
}
@EventHandler
public void deleteFriend(DeletedFriendEvent event) {
AreaShop.debug("DeletedFriendEvent: region " + event.getRegion().getName() + ", " + event.getFriend().getName() + " by " + (event.getBy() == null ? "<nobody>" : event.getBy().getName()));
}
}
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.features;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.regions.GeneralRegion;
import javax.annotation.Nonnull;
public interface FeatureFactory {
@Nonnull
SignsFeature createSignsFeature(@Nonnull GeneralRegion generalRegion);
@Nonnull
TeleportFeature createTeleportFeature(@Nonnull GeneralRegion generalRegion);
@Nonnull
FriendsFeature createFriendsFeature(@Nonnull GeneralRegion generalRegion);
}
@@ -0,0 +1,120 @@
package me.wiefferink.areashop.features;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.events.askandnotify.AddedFriendEvent;
import me.wiefferink.areashop.events.askandnotify.DeletedFriendEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class FriendsFeature extends RegionFeature {
@Inject
private MessageBridge messageBridge;
@AssistedInject
public FriendsFeature(@Nonnull AreaShop plugin, @Assisted @Nonnull GeneralRegion region) {
super(plugin);
setRegion(region);
}
/**
* Add a friend to the region.
* @param player The UUID of the player to add
* @param by The CommandSender that is adding the friend, or null
* @return true if the friend has been added, false if adding a friend was cancelled by another plugin
*/
public boolean addFriend(UUID player, CommandSender by) {
// Fire and check event
AddedFriendEvent event = new AddedFriendEvent(getRegion(), Bukkit.getOfflinePlayer(player), by);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
messageBridge.message(by, "general-cancelled", event.getReason(), this);
return false;
}
Set<String> friends = new HashSet<>(getRegion().getConfig().getStringList("general.friends"));
friends.add(player.toString());
List<String> list = new ArrayList<>(friends);
getRegion().setSetting("general.friends", list);
return true;
}
/**
* Delete a friend from the region.
* @param player The UUID of the player to delete
* @param by The CommandSender that is adding the friend, or null
* @return true if the friend has been added, false if adding a friend was cancelled by another plugin
*/
public boolean deleteFriend(UUID player, CommandSender by) {
// Fire and check event
DeletedFriendEvent event = new DeletedFriendEvent(getRegion(), Bukkit.getOfflinePlayer(player), by);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
messageBridge.message(by, "general-cancelled", event.getReason(), this);
return false;
}
Set<String> friends = new HashSet<>(getRegion().getConfig().getStringList("general.friends"));
friends.remove(player.toString());
List<String> list = new ArrayList<>(friends);
if(list.isEmpty()) {
getRegion().setSetting("general.friends", null);
} else {
getRegion().setSetting("general.friends", list);
}
return true;
}
/**
* Get the list of friends added to this region.
* @return Friends added to this region
*/
public Set<UUID> getFriends() {
HashSet<UUID> result = new HashSet<>();
for(String friend : getRegion().getConfig().getStringList("general.friends")) {
try {
UUID id = UUID.fromString(friend);
result.add(id);
} catch(IllegalArgumentException e) {
// Don't add it
}
}
return result;
}
/**
* Get the list of friends added to this region.
* @return Friends added to this region
*/
public Set<String> getFriendNames() {
HashSet<String> result = new HashSet<>();
for(UUID friend : getFriends()) {
OfflinePlayer player = Bukkit.getOfflinePlayer(friend);
if(player != null && player.getName() != null) {
result.add(player.getName());
}
}
return result;
}
/**
* Remove all friends that are added to this region.
*/
public void clearFriends() {
getRegion().setSetting("general.friends", null);
}
}
@@ -0,0 +1,6 @@
# Features
This package contains classes that represent a certain feature and implement it for all regions.
* `RegionFeature` represents a single feature, for each implementing class one instance is created without an assigned region, to listen for events etc, and an instance per region is created on-demand to handle region specific things.
* More features will be moved from `GeneralRegion` to these kind of classes, to keep all code for a feature together and cleanup `GeneralRegion`.
@@ -0,0 +1,77 @@
package me.wiefferink.areashop.features;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import javax.annotation.Nonnull;
/*
Possible future feature classes:
- RestrictedToRegion feature
- rent/buy restricted to the region itself
- RestrictedToWorld feature
- rent/buy restricted to the world of the region
- CountLimitsFeature:
- Check region limits
- TimeLimitsFeatures:
- maxExtends
- maxRentTime
- SchematicFeature
- save/load schematics
- CommandsFeature
- execute commands after extend/rent/etc
- LandlordFeature
- manage landlords
- ExpirationWarningsFeature
- sending rental expiration warnings
*/
public abstract class RegionFeature implements Listener {
protected final AreaShop plugin;
private GeneralRegion region;
protected RegionFeature(@Nonnull AreaShop plugin) {
this.plugin = plugin;
}
/**
* Set the region for this feature.
* @param region Feature region
*/
public void setRegion(GeneralRegion region) {
this.region = region;
}
/**
* Get the region of this feature.
* @return region of this feature, or null if generic
*/
public GeneralRegion getRegion() {
return region;
}
/**
* Start listening to events.
*/
public void listen() {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
/**
* Destroy the feature and deregister everything.
*/
public void shutdownFeature() {
HandlerList.unregisterAll(this);
shutdown();
}
/**
* Dummy method a RegionFeature implementation can override.
*/
public void shutdown() {
}
}
@@ -0,0 +1,572 @@
package me.wiefferink.areashop.features;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import jakarta.inject.Inject;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignManager;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.areashop.tools.Value;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Set;
public class TeleportFeature extends RegionFeature {
@Inject
private WorldGuardInterface worldGuardInterface;
@AssistedInject
public TeleportFeature(@Nonnull AreaShop plugin, @Assisted @Nonnull GeneralRegion region) {
super(plugin);
setRegion(region);
}
/**
* Get the teleportlocation set for this region.
* @return The teleport location, or null if not set
*/
public Location getTeleportLocation() {
return Utils.configToLocation(getRegion().getConfigurationSectionSetting("general.teleportLocation"));
}
/**
* Check if the region has a teleportLocation specified.
* @return true if the region has a teleportlocation, false otherwise
*/
public boolean hasTeleportLocation() {
return getRegion().getConfigurationSectionSetting("general.teleportLocation") != null;
}
/**
* Set the teleport location of this region.
* @param location The location to set as teleport location
*/
public void setTeleport(Location location) {
if(location == null) {
getRegion().setSetting("general.teleportLocation", null);
} else {
getRegion().setSetting("general.teleportLocation", Utils.locationToConfig(location, true));
}
}
/**
* Teleport a player to the region or sign.
* @param player Player that should be teleported
* @param toSign true to teleport to the first sign of the region, false for teleporting to the region itself
* @param checkRestrictions Set to true if teleport permissions should be checked, false otherwise, also toggles cross-world check
* @return true if the teleport succeeded, otherwise false
*/
public boolean teleportPlayer(Player player, boolean toSign, boolean checkRestrictions) {
// Check basics
if(getRegion().getWorld() == null) {
getRegion().message(player, "general-noWorld");
return false;
}
if(getRegion().getRegion() == null) {
getRegion().message(player, "general-noRegion");
return false;
}
if(checkRestrictions) {
// Check correct world
if(!getRegion().getBooleanSetting("general.teleportCrossWorld") && !player.getWorld().equals(getRegion().getWorld())) {
getRegion().message(player, "teleport-wrongWorld", player.getWorld().getName());
return false;
}
boolean owner = player.getUniqueId().equals(getRegion().getOwner());
boolean friend = getRegion().getFriendsFeature().getFriends().contains(player.getUniqueId());
boolean available = getRegion().isAvailable();
// Teleport to sign instead if they dont have permission for teleporting to region
if((!toSign && owner && !player.hasPermission("areashop.teleport") && player.hasPermission("areashop.teleportsign")
|| !toSign && !owner && !friend && !player.hasPermission("areashop.teleportall") && player.hasPermission("areashop.teleportsignall")
|| !toSign && !owner && friend && !player.hasPermission("areashop.teleportfriend") && player.hasPermission("areashop.teleportfriendsign")
|| !toSign && !owner && !friend && available && !player.hasPermission("areashop.teleportavailable") && player.hasPermission("areashop.teleportavailablesign"))) {
getRegion().message(player, "teleport-changedToSign");
toSign = true;
}
// Check permissions
if(owner && !available && !player.hasPermission("areashop.teleport") && !toSign) {
getRegion().message(player, "teleport-noPermission");
return false;
} else if(!owner && !available && !player.hasPermission("areashop.teleportall") && !toSign && !friend) {
getRegion().message(player, "teleport-noPermissionOther");
return false;
} else if(!owner && !available && !player.hasPermission("areashop.teleportfriend") && !toSign && friend) {
getRegion().message(player, "teleport-noPermissionFriend");
return false;
} else if(available && !player.hasPermission("areashop.teleportavailable") && !toSign) {
getRegion().message(player, "teleport-noPermissionAvailable");
return false;
} else if(owner && !available && !player.hasPermission("areashop.teleportsign") && toSign) {
getRegion().message(player, "teleport-noPermissionSign");
return false;
} else if(!owner && !available && !player.hasPermission("areashop.teleportsignall") && toSign && !friend) {
getRegion().message(player, "teleport-noPermissionOtherSign");
return false;
} else if(!owner && !available && !player.hasPermission("areashop.teleportfriendsign") && toSign && friend) {
getRegion().message(player, "teleport-noPermissionFriendSign");
return false;
} else if(available && !player.hasPermission("areashop.teleportavailablesign") && toSign) {
getRegion().message(player, "teleport-noPermissionAvailableSign");
return false;
}
}
// Get the starting location
Value<Boolean> toSignRef = new Value<>(toSign);
Location startLocation = getStartLocation(player, toSignRef);
toSign = toSignRef.get();
boolean insideRegion;
if(toSign) {
insideRegion = getRegion().getBooleanSetting("general.teleportToSignIntoRegion");
} else {
insideRegion = getRegion().getBooleanSetting("general.teleportIntoRegion");
}
// Check locations starting from startLocation and then a cube that increases
// radius around that (until no block in the region is found at all cube sides)
Location safeLocation = startLocation;
ProtectedRegion worldguardRegion = getRegion().getRegion();
boolean blocksInRegion = worldguardRegion.contains(startLocation.getBlockX(), startLocation.getBlockY(), startLocation.getBlockZ());
if(!blocksInRegion && insideRegion) {
getRegion().message(player, "teleport-blocked");
return false;
}
// Tries limit tracking
int radius = 1;
int checked = 1;
int maxTries = plugin.getConfig().getInt("maximumTries");
// Tracking of which sides to continue the search
boolean done = isSafe(safeLocation);
boolean northDone = false, eastDone = false, southDone = false, westDone = false, topDone = false, bottomDone = false;
boolean continueThisDirection;
while((blocksInRegion || !insideRegion) && !done) {
blocksInRegion = false;
// North side
continueThisDirection = false;
for(int x = -radius + 1; x <= radius && !done && !northDone; x++) {
for(int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(x, y, -radius);
if(safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
}
northDone = northDone || !continueThisDirection;
// East side
continueThisDirection = false;
for(int z = -radius + 1; z <= radius && !done && !eastDone; z++) {
for(int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(radius, y, z);
if(safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
}
eastDone = eastDone || !continueThisDirection;
// South side
continueThisDirection = false;
for(int x = radius - 1; x >= -radius && !done && !southDone; x--) {
for(int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(x, y, radius);
if(safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
}
southDone = southDone || !continueThisDirection;
// West side
continueThisDirection = false;
for(int z = radius - 1; z >= -radius && !done && !westDone; z--) {
for(int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(-radius, y, z);
if(safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
}
westDone = westDone || !continueThisDirection;
// Top side
continueThisDirection = false;
// Middle block of the top
if((startLocation.getBlockY() + radius) > 256) {
topDone = true;
}
if(!done && !topDone) {
safeLocation = startLocation.clone().add(0, radius, 0);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
for(int r = 1; r <= radius && !done && !topDone; r++) {
// North
for(int x = -r + 1; x <= r && !done; x++) {
safeLocation = startLocation.clone().add(x, radius, -r);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
// East
for(int z = -r + 1; z <= r && !done; z++) {
safeLocation = startLocation.clone().add(r, radius, z);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
// South side
for(int x = r - 1; x >= -r && !done; x--) {
safeLocation = startLocation.clone().add(x, radius, r);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
// West side
for(int z = r - 1; z >= -r && !done; z--) {
safeLocation = startLocation.clone().add(-r, radius, z);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
}
topDone = topDone || !continueThisDirection;
// Bottom side
continueThisDirection = false;
// Middle block of the bottom
if(startLocation.getBlockY() - radius < 0) {
bottomDone = true;
}
if(!done && !bottomDone) {
safeLocation = startLocation.clone().add(0, -radius, 0);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
for(int r = 1; r <= radius && !done && !bottomDone; r++) {
// North
for(int x = -r + 1; x <= r && !done; x++) {
safeLocation = startLocation.clone().add(x, -radius, -r);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
// East
for(int z = -r + 1; z <= r && !done; z++) {
safeLocation = startLocation.clone().add(r, -radius, z);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
// South side
for(int x = r - 1; x >= -r && !done; x--) {
safeLocation = startLocation.clone().add(x, -radius, r);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
// West side
for(int z = r - 1; z >= -r && !done; z--) {
safeLocation = startLocation.clone().add(-r, -radius, z);
if(worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegion = true;
continueThisDirection = true;
}
}
}
bottomDone = bottomDone || !continueThisDirection;
// Increase cube radius
radius++;
}
if(done && isSafe(safeLocation)) {
if(toSign) {
getRegion().message(player, "teleport-successSign");
// Let the player look at the sign
Vector playerVector = safeLocation.toVector();
playerVector.setY(playerVector.getY() + player.getEyeHeight(true));
SignManager signManager = getRegion().getSignsFeature().signManager();
Iterator<RegionSign> iterator = signManager.allSigns().iterator();
Vector signVector = iterator.next().getLocation().toVector().add(new Vector(0.5, 0.5, 0.5));
Vector direction = playerVector.clone().subtract(signVector).normalize();
safeLocation.setYaw(180 - (float)Math.toDegrees(Math.atan2(direction.getX(), direction.getZ())));
safeLocation.setPitch(90 - (float)Math.toDegrees(Math.acos(direction.getY())));
} else {
getRegion().message(player, "teleport-success");
}
player.teleport(safeLocation);
AreaShop.debug("Found location: " + safeLocation.toString() + " Tries: " + (checked - 1));
return true;
} else {
getRegion().message(player, "teleport-noSafe", checked - 1, maxTries);
AreaShop.debug("No location found, checked " + (checked - 1) + " spots of max " + maxTries);
return false;
}
}
/**
* Teleport a player to the region or sign when he has permissions for it.
* @param player Player that should be teleported
* @param toSign true to teleport to the first sign of the region, false for teleporting to the region itself
* @return true if the teleport succeeded, otherwise false
*/
public boolean teleportPlayer(Player player, boolean toSign) {
return teleportPlayer(player, toSign, true);
}
/**
* Teleport a player to the region when he has permissions for it.
* @param player Player that should be teleported
* @return true if the teleport succeeded, otherwise false
*/
public boolean teleportPlayer(Player player) {
return teleportPlayer(player, false, true);
}
/**
* Checks if a certain location is safe to teleport to.
* @param location The location to check
* @return true if it is safe, otherwise false
*/
private boolean isSafe(Location location) {
Block feet = location.getBlock();
Block head = feet.getRelative(BlockFace.UP);
Block below = feet.getRelative(BlockFace.DOWN);
Block above = head.getRelative(BlockFace.UP);
// Check the block at the feet and head of the player
if((feet.getType().isSolid() && !canSpawnIn(feet.getType())) || feet.isLiquid()) {
return false;
} else if((head.getType().isSolid() && !canSpawnIn(head.getType())) || head.isLiquid()) {
return false;
} else if(!below.getType().isSolid() || cannotSpawnOn(below.getType()) || below.isLiquid()) {
return false;
} else if(above.isLiquid() || cannotSpawnBeside(above.getType())) {
return false;
}
// Get all blocks around the player (below foot level, foot level, head level and above head level)
Set<Material> around = EnumSet.noneOf(Material.class);
for(int y = 0; y <= 3; y++) {
for(int x = -1; x <= 1; x++) {
for(int z = -1; z <= 1; z++) {
// Skip blocks in the column of the player
if(x == 0 && z == 0) {
continue;
}
around.add(below.getRelative(x, y, z).getType());
}
}
}
// Check the blocks around the player
for(Material material : around) {
if(cannotSpawnBeside(material)) {
return false;
}
}
return true;
}
/**
* Check if a player can spawn in here.
* @param material Material to check (assumed that this is at the feet or head level)
* @return true when it is safe to spawn inside, otherwise false
*/
private static boolean canSpawnIn(Material material) {
return material == Material.DRAGON_EGG
|| Tag.DOORS.isTagged(material)
|| Tag.SIGNS.isTagged(material)
|| Tag.PRESSURE_PLATES.isTagged(material);
}
/**
* Check if a player can spawn on here.
* @param material Material to check (assumed that this is below the feet)
* @return true when it is safe to spawn on top of, otherwise false
*/
private static boolean cannotSpawnOn(Material material) {
return material == Material.CACTUS
|| material == Material.PISTON
|| material == Material.PISTON_HEAD
|| material == Material.MOVING_PISTON
|| material == Material.DRAGON_EGG
|| material == Material.MAGMA_BLOCK
|| material == Material.REDSTONE_LAMP
|| Tag.SIGNS.isTagged(material)
|| Tag.DOORS.isTagged(material)
|| Tag.FENCE_GATES.isTagged(material)
|| Tag.FENCES.isTagged(material)
|| material.name().contains("GLASS_PANE");
}
/**
* Check if a player can spawn next to it.
* @param material Material to check (assumed that this is somewhere around the player)
* @return true when it is safe to spawn next to, otherwise false
*/
private static boolean cannotSpawnBeside(Material material) {
return material == Material.LAVA
|| material == Material.FIRE
|| material == Material.MAGMA_BLOCK;
}
/**
* Get the start location of a safe teleport search.
* @param player The player to get it for
* @param toSign true to try teleporting to the first sign, false for teleporting to the region
* @return The start location
*/
private Location getStartLocation(Player player, Value<Boolean> toSign) {
Location startLocation = null;
ProtectedRegion worldguardRegion = getRegion().getRegion();
// Try to get sign location
Collection<RegionSign> signs = getRegion().getSignsFeature().signManager().allSigns();
if(toSign.get()) {
boolean signAvailable = !signs.isEmpty();
if(signAvailable) {
RegionSign firstSign = signs.iterator().next();
// Use the location 1 below the sign to prevent weird spawing above the sign
startLocation = firstSign.getLocation(); //.subtract(0.0, 1.0, 0.0);
startLocation.setPitch(player.getLocation().getPitch());
startLocation.setYaw(player.getLocation().getYaw());
// Move player x blocks away from the sign
double distance = getRegion().getDoubleSetting("general.teleportSignDistance");
if(distance > 0) {
BlockFace facing = firstSign.getFacing();
Vector facingVector = new Vector(facing.getModX(), facing.getModY(), facing.getModZ())
.normalize()
.multiply(distance);
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
startLocation.add(facingVector);
}
} else {
// No sign available
getRegion().message(player, "teleport-changedToNoSign");
toSign.set(false);
}
}
// Use teleportation location that is set for the region
if(startLocation == null && hasTeleportLocation()) {
startLocation = getTeleportLocation();
}
// Calculate a default location
if(startLocation == null) {
// Set to block in the middle, y configured in the config
Vector regionMin = worldGuardInterface.getMinimumPoint(worldguardRegion);
Vector regionMax = worldGuardInterface.getMaximumPoint(worldguardRegion);
Vector middle = regionMin.clone().midpoint(regionMax);
String configSetting = getRegion().getStringSetting("general.teleportLocationY");
if("bottom".equalsIgnoreCase(configSetting)) {
middle = middle.setY(regionMin.getBlockY());
} else if("top".equalsIgnoreCase(configSetting)) {
middle = middle.setY(regionMax.getBlockY());
} else if("middle".equalsIgnoreCase(configSetting)) {
middle = middle.setY(middle.getBlockY());
} else {
try {
int vertical = Integer.parseInt(configSetting);
middle = middle.setY(vertical);
} catch(NumberFormatException e) {
AreaShop.warn("Could not parse general.teleportLocationY: '" + configSetting + "'");
}
}
startLocation = new Location(getRegion().getWorld(), middle.getX(), middle.getY(), middle.getZ(), player.getLocation().getYaw(), player.getLocation().getPitch());
}
// Set location in the center of the block
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
return startLocation;
}
}
@@ -0,0 +1,278 @@
package me.wiefferink.areashop.features;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.flags.InvalidFlagFormat;
import com.sk89q.worldguard.protection.flags.RegionGroupFlag;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.interfaces.RegionAccessSet;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import javax.annotation.Nonnull;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@Singleton
public class WorldGuardRegionFlagsFeature extends RegionFeature {
@Inject
private WorldGuardInterface worldGuardInterface;
@Inject
private WorldGuardPlugin worldGuard;
@Inject
WorldGuardRegionFlagsFeature(@Nonnull AreaShop plugin) {
super(plugin);
}
@EventHandler
public void regionUpdate(UpdateRegionEvent event) {
updateRegionFlags(event.getRegion());
}
/**
* Set the region flags/options to the values of a ConfigurationSection.
* @param region The region to update the flags for
* @return true if the flags have been set correctly, otherwise false
*/
private boolean updateRegionFlags(GeneralRegion region) {
boolean result = true;
// Get section defining the region flag profile
ConfigurationSection flagProfileSection = region.getConfigurationSectionSetting("general.flagProfile", "flagProfiles");
if(flagProfileSection == null) {
return false;
}
// Region flags for all states
ConfigurationSection allFlags = flagProfileSection.getConfigurationSection("ALL");
if(allFlags != null) {
result = updateRegionFlags(region, allFlags);
}
// Region flags for the current state
ConfigurationSection stateFlags = flagProfileSection.getConfigurationSection(region.getState().getValue());
// If in reselling mode, fallback to 'resale' section if 'resell' is not found (legacy configuration problem: https://github.com/NLthijs48/AreaShop/issues/303)
if(stateFlags == null && region.getState() == GeneralRegion.RegionState.RESELL) {
stateFlags = flagProfileSection.getConfigurationSection("resale");
}
if(stateFlags != null) {
result &= updateRegionFlags(region, stateFlags);
}
return result;
}
/**
* Set the region flags/options to the values of a ConfigurationSection.
* @param region The region to update the flags for
* @param flags The flags to apply
* @return true if the flags have been set correctly, otherwise false
*/
private boolean updateRegionFlags(GeneralRegion region, ConfigurationSection flags) {
boolean result = true;
Set<String> flagNames = flags.getKeys(false);
// Get the region
ProtectedRegion worldguardRegion = region.getRegion();
if(worldguardRegion == null) {
AreaShop.debug("Region '" + region.getName() + "' does not exist, setting flags failed");
return false;
}
// Loop through all flags that are set in the config
for(String flagName : flagNames) {
String value = Message.fromString(flags.getString(flagName)).replacements(region).getPlain();
// In the config normal Bukkit color codes are used, those only need to be translated on 5.X WorldGuard versions
if(worldGuard.getDescription().getVersion().startsWith("5.")) {
value = translateBukkitToWorldGuardColors(value);
}
if(flagName.equalsIgnoreCase("members")) {
worldGuardInterface.setMembers(worldguardRegion, parseAccessSet(value));
//AreaShop.debug(" Flag " + flagName + " set: " + members.toUserFriendlyString());
} else if(flagName.equalsIgnoreCase("owners")) {
worldGuardInterface.setOwners(worldguardRegion, parseAccessSet(value));
//AreaShop.debug(" Flag " + flagName + " set: " + owners.toUserFriendlyString());
} else if(flagName.equalsIgnoreCase("priority")) {
try {
int priority = Integer.parseInt(value);
if(worldguardRegion.getPriority() != priority) {
worldguardRegion.setPriority(priority);
}
//AreaShop.debug(" Flag " + flagName + " set: " + value);
} catch(NumberFormatException e) {
AreaShop.warn("The value of flag " + flagName + " is not a number");
result = false;
}
} else if(flagName.equalsIgnoreCase("parent")) {
if(region.getWorld() == null || plugin.getRegionManager(region.getWorld()) == null) {
continue;
}
ProtectedRegion parentRegion = plugin.getRegionManager(region.getWorld()).getRegion(value);
if(parentRegion != null) {
if(!parentRegion.equals(worldguardRegion.getParent())) {
try {
worldguardRegion.setParent(parentRegion);
//AreaShop.debug(" Flag " + flagName + " set: " + value);
} catch(ProtectedRegion.CircularInheritanceException e) {
AreaShop.warn("The parent set in the config is not correct (circular inheritance)");
}
}
} else {
AreaShop.warn("The parent set in the config is not correct (region does not exist)");
}
} else {
// Parse all other normal flags (groups are also handled)
String flagSetting = null;
com.sk89q.worldguard.protection.flags.RegionGroup groupValue = null;
Flag<?> foundFlag = worldGuardInterface.fuzzyMatchFlag(flagName);
if(foundFlag == null) {
AreaShop.warn("Found wrong flag in flagProfiles section: " + flagName + ", check if that is the correct WorldGuard flag");
continue;
}
RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag();
if(value == null || value.isEmpty()) {
if(worldguardRegion.getFlag(foundFlag) != null) {
worldguardRegion.setFlag(foundFlag, null);
}
if(groupFlag != null && worldguardRegion.getFlag(groupFlag) != null) {
worldguardRegion.setFlag(groupFlag, null);
}
//AreaShop.debug(" Flag " + flagName + " reset (+ possible group of flag)");
} else {
if(groupFlag == null) {
flagSetting = value;
} else {
for(String part : value.split(" ")) {
if(part.startsWith("g:")) {
if(part.length() > 2) {
try {
groupValue = worldGuardInterface.parseFlagGroupInput(groupFlag, part.substring(2));
} catch(InvalidFlagFormat e) {
AreaShop.warn("Found wrong group value for flag " + flagName);
}
}
} else {
if(flagSetting == null) {
flagSetting = part;
} else {
flagSetting += " " + part;
}
}
}
}
if(flagSetting != null) {
try {
setFlag(worldguardRegion, foundFlag, flagSetting);
//AreaShop.debug(" Flag " + flagName + " set: " + flagSetting);
} catch(InvalidFlagFormat e) {
AreaShop.warn("Found wrong value for flag " + flagName);
}
}
if(groupValue != null) {
if(groupValue == groupFlag.getDefault()) {
worldguardRegion.setFlag(groupFlag, null);
//AreaShop.debug(" Group of flag " + flagName + " set to default: " + groupValue);
} else {
worldguardRegion.setFlag(groupFlag, groupValue);
//AreaShop.debug(" Group of flag " + flagName + " set: " + groupValue);
}
}
}
}
}
// Indicate that the regions needs to be saved
if(worldGuard.getDescription().getVersion().startsWith("5.")) {
plugin.getFileManager().saveIsRequiredForRegionWorld(region.getWorldName());
}
return result;
}
/**
* Build an RegionAccessSet from an input that specifies player names, player uuids and groups.
* @param input Input string defining the access set
* @return RegionAccessSet containing the entities parsed from the input
*/
public RegionAccessSet parseAccessSet(String input) {
RegionAccessSet result = new RegionAccessSet();
String[] inputParts = input.split(", ");
for(String access : inputParts) {
if(access != null && !access.isEmpty()) {
// Check for groups
if(access.startsWith("g:")) {
if(access.length() > 2) {
result.getGroupNames().add(access.substring(2));
}
} else if(access.startsWith("n:")) {
if(access.length() > 2) {
result.getPlayerNames().add(access.substring(2));
}
} else {
try {
result.getPlayerUniqueIds().add(UUID.fromString(access));
} catch(IllegalArgumentException e) {
AreaShop.warn("Tried using '" + access + "' as uuid for a region member/owner, is your flagProfiles section correct?");
}
}
}
}
return result;
}
/**
* Set a WorldGuard region flag.
* @param region The WorldGuard region to set
* @param flag The flag to set
* @param value The value to set the flag to
* @param <V> They type of flag to set
* @throws InvalidFlagFormat When the value of the flag is wrong
*/
private <V> void setFlag(ProtectedRegion region, Flag<V> flag, String value) throws InvalidFlagFormat {
V current = region.getFlag(flag);
V next = worldGuardInterface.parseFlagInput(flag, value);
if(!Objects.equals(current, next)) {
region.setFlag(flag, next);
}
}
/**
* Translate the color codes you put in greeting/farewell messages to the weird color codes of WorldGuard.
* @param message The message where the color codes should be translated (this message has bukkit color codes)
* @return The string with the WorldGuard color codes
*/
private String translateBukkitToWorldGuardColors(String message) {
String result = message;
result = result.replace("&c", "&r");
result = result.replace("&4", "&R");
result = result.replace("&e", "&y");
result = result.replace("&6", "&Y");
result = result.replace("&a", "&g");
result = result.replace("&2", "&G");
result = result.replace("&b", "&c");
result = result.replace("&3", "&C");
result = result.replace("&9", "&b");
result = result.replace("&1", "&B");
result = result.replace("&d", "&p");
result = result.replace("&5", "&P");
result = result.replace("&0", "&0");
result = result.replace("&8", "&1");
result = result.replace("&7", "&2");
result = result.replace("&f", "&w");
result = result.replace("&r", "&x");
return result;
}
}
@@ -0,0 +1,295 @@
package me.wiefferink.areashop.features.signs;
import com.google.common.base.Objects;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import io.github.bakedlibs.dough.blocks.BlockPosition;
import io.papermc.lib.PaperLib;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.managers.SignErrorLogger;
import me.wiefferink.areashop.nms.BlockBehaviourHelper;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.SignUtils;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.Sign;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
/**
* Sign that is connected to a region to display information and interact with the region.
*/
public class RegionSign {
private final BlockBehaviourHelper blockHelper;
private final SignErrorLogger errorLogger;
private final SignsFeature signsFeature;
private final String key;
@AssistedInject
RegionSign(
@Nonnull BlockBehaviourHelper blockBehaviourHelper,
@Nonnull SignErrorLogger signErrorLogger,
@Assisted @Nonnull SignsFeature signsFeature,
@Assisted @Nonnull String key
) {
this.blockHelper = blockBehaviourHelper;
this.errorLogger = signErrorLogger;
this.signsFeature = signsFeature;
this.key = key;
}
public BlockPosition getPosition() {
return new BlockPosition(getLocation());
}
/**
* Get the location of this sign.
* @return The location of this sign
*/
public Location getLocation() {
return Utils.configToLocation(getRegion().getConfig().getConfigurationSection("general.signs." + key + ".location"));
}
/**
* Location string to be used as key in maps.
* @return Location string
*/
public String getStringLocation() {
return SignsFeature.locationToString(getLocation());
}
/**
* Chunk string to be used as key in maps.
* @return Chunk string
*/
public String getStringChunk() {
return SignsFeature.chunkToString(getLocation());
}
/**
* Get the region this sign is linked to.
* @return The region this sign is linked to
*/
public GeneralRegion getRegion() {
return signsFeature.getRegion();
}
/**
* Remove this sign from the region.
*/
public void remove() {
Location location = getLocation();
if (location != null && location.getWorld() != null) {
location.getBlock().setType(Material.AIR);
this.signsFeature.signManager().removeSign(this);
}
getRegion().setSetting("general.signs." + key, null);
// Remove the sign from the region's sign manager
}
/**
* Get the ConfigurationSection defining the sign layout.
* @return The sign layout config
*/
public ConfigurationSection getProfile() {
return getRegion().getConfigurationSectionSetting("general.signProfile", "signProfiles", getRegion().getConfig().get("general.signs." + key + ".profile"));
}
/**
* Get the facing of the sign as saved in the config.
* @return BlockFace the sign faces, or null if unknown
*/
public BlockFace getFacing() {
try {
return BlockFace.valueOf(getRegion().getConfig().getString("general.signs." + key + ".facing"));
} catch(NullPointerException | IllegalArgumentException e) {
return null;
}
}
/**
* Get the material of the sign as saved in the config.
* @return Material of the sign,Material.AIR if none.
*/
public Material getMaterial() {
String name = getRegion().getConfig().getString("general.signs." + key + ".signType");
Material result = Materials.signNameToMaterial(name);
return result == null ? Material.AIR : result;
}
/**
* Update this sign.
* @return true if the update was successful, otherwise false
*/
public boolean update() {
// Ignore updates of signs in chunks that are not loaded
Location signLocation = getLocation();
if(signLocation == null
|| signLocation.getWorld() == null
|| !signLocation.getWorld().isChunkLoaded(signLocation.getBlockX() >> 4, signLocation.getBlockZ() >> 4)) {
return false;
}
if(getRegion().isDeleted()) {
return false;
}
YamlConfiguration regionConfig = getRegion().getConfig();
ConfigurationSection signConfig = getProfile();
Block block = signLocation.getBlock();
if(signConfig == null || !signConfig.isSet(getRegion().getState().getValue())) {
block.setType(Material.AIR);
return true;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue());
// Get the lines
String[] signLines = new String[4];
boolean signEmpty = true;
for(int i = 0; i < 4; i++) {
signLines[i] = stateConfig.getString("line" + (i + 1));
signEmpty &= (signLines[i] == null || signLines[i].isEmpty());
}
if(signEmpty) {
block.setType(Material.AIR);
return true;
}
// Place the sign back (with proper rotation and type) after it has been hidden or (indirectly) destroyed
if(!Materials.isSign(block.getType())) {
Material signType = getMaterial();
if (!blockHelper.canPlace(block.getLocation(), Bukkit.createBlockData(signType))) {
errorLogger.submitWarning("Setting sign" + key + "of region" + getRegion().getName() + "failed, could not set sign block back");
this.remove();
return false;
}
// Don't do physics here, we first need to update the direction
block.setType(signType, false);
BlockState blockState = PaperLib.getBlockState(block, false).getState();
BlockData blockData = blockState.getBlockData();
if(blockData instanceof WallSign) {
((WallSign) blockData).setFacing(getFacing());
} else if(blockData instanceof org.bukkit.block.data.type.Sign) {
((org.bukkit.block.data.type.Sign) blockData).setRotation(getFacing());
} else {
errorLogger.submitWarning("Failed to update the facing direction of the sign at" + getStringLocation() + "to " + getFacing() + ", region:" + getRegion().getName());
return false;
}
block.setBlockData(blockData);
// Check if the sign has popped
if(!Materials.isSign(block.getType())) {
return false;
}
}
// Save current rotation and type
if(!regionConfig.isString("general.signs." + key + ".signType")) {
getRegion().setSetting("general.signs." + key + ".signType", block.getType().name());
}
if(!regionConfig.isString("general.signs." + key + ".facing")) {
BlockFace signFacing = SignUtils.getSignFacing(block);
getRegion().setSetting("general.signs." + key + ".facing", signFacing == null ? null : signFacing.toString());
}
// Apply replacements and color and then set it on the sign
Sign signState = (Sign) PaperLib.getBlockState(block, false).getState();
for(int i = 0; i < signLines.length; i++) {
if(signLines[i] == null) {
signState.setLine(i, "");
continue;
}
signLines[i] = Message.fromString(signLines[i]).replacements(getRegion()).getSingle();
signLines[i] = Utils.applyColors(signLines[i]);
signState.setLine(i, signLines[i]);
}
signState.update(false, false);
return true;
}
/**
* Check if the sign needs to update periodically.
* @return true if it needs periodic updates, otherwise false
*/
public boolean needsPeriodicUpdate() {
ConfigurationSection signConfig = getProfile();
if(signConfig == null || !signConfig.isSet(getRegion().getState().getValue().toLowerCase())) {
return false;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue().toLowerCase());
if(stateConfig == null) {
return false;
}
// Check the lines for the timeleft tag
for(int i = 1; i <= 4; i++) {
String line = stateConfig.getString("line" + i);
if(line != null && line.contains(Message.VARIABLE_START + AreaShop.tagTimeLeft + Message.VARIABLE_END)) {
return true;
}
}
return false;
}
/**
* Run commands when a player clicks a sign.
* @param clicker The player that clicked the sign
* @param clickType The type of clicking
* @return true if the commands ran successfully, false if any of them failed
*/
public boolean runSignCommands(Player clicker, GeneralRegion.ClickType clickType) {
ConfigurationSection signConfig = getProfile();
if(signConfig == null) {
return false;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue().toLowerCase());
// Run player commands if specified
List<String> playerCommands = new ArrayList<>();
for(String command : stateConfig.getStringList(clickType.getValue() + "Player")) {
// TODO move variable checking code to InteractiveMessenger?
playerCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName()));
}
getRegion().runCommands(clicker, playerCommands);
// Run console commands if specified
List<String> consoleCommands = new ArrayList<>();
for(String command : stateConfig.getStringList(clickType.getValue() + "Console")) {
consoleCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName()));
}
getRegion().runCommands(Bukkit.getConsoleSender(), consoleCommands);
return !playerCommands.isEmpty() || !consoleCommands.isEmpty();
}
@Override
public boolean equals(Object object) {
return object instanceof RegionSign && ((RegionSign)object).getRegion().equals(this.getRegion()) && ((RegionSign)object).key.equals(this.key);
}
@Override
public int hashCode() {
return Objects.hashCode(key, getRegion().getName());
}
}
@@ -0,0 +1,81 @@
package me.wiefferink.areashop.features.signs;
import io.github.bakedlibs.dough.blocks.BlockPosition;
import io.github.bakedlibs.dough.blocks.ChunkPosition;
import org.bukkit.Location;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
public class SignCache {
private final Map<Long, RegionSign> allSigns = new HashMap<>();
private final Map<Long, Collection<RegionSign>> signsByChunk = new HashMap<>();
public Optional<RegionSign> signAtLocation(long position) {
return Optional.ofNullable(this.allSigns.get(position));
}
public Collection<RegionSign> signsAtChunk(long position) {
return Collections.unmodifiableCollection(this.signsByChunk.getOrDefault(position, Collections.emptyList()));
}
public Collection<RegionSign> allSigns() {
return Collections.unmodifiableCollection(this.allSigns.values());
}
public Collection<BlockPosition> allSignLocations() {
Collection<BlockPosition> positions = new ArrayList<>(this.signsByChunk.size());
for (RegionSign sign : this.allSigns.values()) {
positions.add(sign.getPosition());
}
return positions;
}
public void addSign(RegionSign regionSign) {
BlockPosition blockPosition = regionSign.getPosition();
this.allSigns.put(blockPosition.getPosition(), regionSign);
ChunkPosition chunkPosition = new ChunkPosition(blockPosition.getChunk());
Collection<RegionSign> signs = this.signsByChunk.computeIfAbsent(chunkPosition.getPosition(), x -> new HashSet<>());
signs.add(regionSign);
}
public Optional<RegionSign> removeSign(Location location) {
return removeSign(new BlockPosition(location));
}
public Optional<RegionSign> removeSign(BlockPosition position) {
RegionSign removed = this.allSigns.remove(position.getPosition());
if (removed != null) {
Location location = position.toLocation();
this.signsByChunk.remove(new ChunkPosition(location).getPosition());
// Remove the sign from the region
}
return Optional.ofNullable(removed);
}
public void removeSign(RegionSign regionSign) {
BlockPosition blockPosition = regionSign.getPosition();
this.allSigns.remove(blockPosition.getPosition());
ChunkPosition chunkPosition = new ChunkPosition(blockPosition.getChunk());
long packedChunkPos = chunkPosition.getPosition();
Collection<RegionSign> signs = this.signsByChunk.get(packedChunkPos);
if (signs != null) {
signs.remove(regionSign);
if (signs.isEmpty()) {
this.signsByChunk.remove(packedChunkPos);
}
}
}
public void clear() {
this.allSigns.clear();
this.signsByChunk.clear();
}
}
@@ -0,0 +1,10 @@
package me.wiefferink.areashop.features.signs;
import javax.annotation.Nonnull;
public interface SignFactory {
@Nonnull RegionSign createRegionSign(@Nonnull SignsFeature signsFeature, @Nonnull String key);
}
@@ -0,0 +1,450 @@
package me.wiefferink.areashop.features.signs;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import io.github.bakedlibs.dough.blocks.ChunkPosition;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.managers.SignLinkerManager;
import me.wiefferink.areashop.nms.BlockBehaviourHelper;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionFactory;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.SignUtils;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.bukkitdo.Do;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public class SignListener implements Listener {
private final BlockBehaviourHelper behaviourHelper;
private final AreaShop plugin;
private final MessageBridge messageBridge;
private final SignManager signManager;
private final SignLinkerManager signLinkerManager;
private final WorldGuardInterface worldGuardInterface;
private final RegionFactory regionFactory;
private final IFileManager fileManager;
public SignListener(
@Nonnull AreaShop plugin,
@Nonnull BlockBehaviourHelper behaviourHelper,
@Nonnull RegionFactory regionFactory,
@Nonnull MessageBridge messageBridge,
@Nonnull SignLinkerManager signLinkerManager,
@Nonnull WorldGuardInterface worldGuardInterface,
@Nonnull SignManager signManager,
@Nonnull IFileManager fileManager) {
this.fileManager = fileManager;
this.signManager = signManager;
this.signLinkerManager = signLinkerManager;
this.regionFactory = regionFactory;
this.behaviourHelper = behaviourHelper;
this.plugin = plugin;
this.worldGuardInterface = worldGuardInterface;
this.messageBridge = messageBridge;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void regionUpdate(UpdateRegionEvent event) {
Optional<SignsFeature> signsFeature = event.getRegion().getFeature(SignsFeature.class);
signsFeature.map(SignsFeature::signManager).ifPresent(SignManager::update);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onChunkLoad(ChunkLoadEvent event) {
Chunk chunk = event.getChunk();
Optional<SignCache> optional = this.signManager.getCacheForWorld(chunk.getWorld());
if (optional.isEmpty()) {
return;
}
final SignCache signCache = optional.get();
Collection<RegionSign> chunkSigns = new ArrayList<>(signCache.signsAtChunk(ChunkPosition.getAsLong(chunk.getX(), chunk.getZ())));
if(chunkSigns.isEmpty()) {
return;
}
Do.forAll(chunkSigns, RegionSign::update);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onIndirectSignBreak(BlockPhysicsEvent event) {
// Check if the block is a sign
if(!Materials.isSign(event.getBlock().getType()) || behaviourHelper.isBlockValid(event.getBlock())) {
return;
}
// Check if the sign is really the same as a saved rent
final Optional<RegionSign> optionalSign = this.signManager.signFromLocation(event.getBlock().getLocation());
if(optionalSign.isEmpty()) {
return;
}
RegionSign regionSign = optionalSign.get();
// Remove the sign so that it does not fall on the floor as an item (next region update will place it back when possible)
AreaShop.debug("onIndirectSignBreak: Removed block of sign for", regionSign.getRegion().getName(), "at", regionSign.getStringLocation());
event.getBlock().setType(Material.AIR);
event.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGH)
public void onSignBreak(BlockBreakEvent event) {
if(event.isCancelled()) {
return;
}
Block block = event.getBlock();
// Check if it is a sign
if(Materials.isSign(block.getType())) {
// Check if the rent sign is really the same as a saved rent
Optional<RegionSign> optional = signManager.signFromLocation(block.getLocation());
if(optional.isEmpty()) {
return;
}
RegionSign regionSign = optional.get();
// Remove the sign of the rental region if the player has permission
if(event.getPlayer().hasPermission("areashop.delsign")) {
signManager.removeSign(regionSign);
messageBridge.message(event.getPlayer(), "delsign-success", regionSign.getRegion());
} else { // Cancel the breaking of the sign
event.setCancelled(true);
messageBridge.message(event.getPlayer(), "delsign-noPermission", regionSign.getRegion());
}
}
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onSignClick(PlayerInteractEvent event) {
Block block = event.getClickedBlock();
if (block == null) {
return;
}
// Only listen to left and right clicks on blocks
if (!(event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK)) {
return;
}
// Only care about clicking blocks
if(!Materials.isSign(block.getType())) {
return;
}
// Check if this sign belongs to a region
Optional<RegionSign> optional = signManager.signFromLocation(block.getLocation());
if(optional.isEmpty()) {
return;
}
RegionSign regionSign = optional.get();
// Ignore players that are in sign link mode (which will handle the event itself)
Player player = event.getPlayer();
if(signLinkerManager.isInSignLinkMode(player)) {
return;
}
// Get the clicktype
GeneralRegion.ClickType clickType = null;
if(player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) {
clickType = GeneralRegion.ClickType.SHIFTLEFTCLICK;
} else if(!player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) {
clickType = GeneralRegion.ClickType.LEFTCLICK;
} else if(player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) {
clickType = GeneralRegion.ClickType.SHIFTRIGHTCLICK;
} else if(!player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) {
clickType = GeneralRegion.ClickType.RIGHTCLICK;
}
boolean ran = regionSign.runSignCommands(player, clickType);
// Only cancel event if at least one command has been executed
event.setCancelled(ran);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSignChange(SignChangeEvent event) {
Player player = event.getPlayer();
if(!plugin.isReady()) {
messageBridge.message(player, "general-notReady");
return;
}
// Check if the sign is meant for this plugin
if(event.getLine(0).contains(plugin.getConfig().getString("signTags.rent"))) {
if(!player.hasPermission("areashop.createrent") && !player.hasPermission("areashop.createrent.member") && !player.hasPermission("areashop.createrent.owner")) {
messageBridge.message(player, "setup-noPermissionRent");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
String fourthLine = event.getLine(3);
// Get the regionManager for accessing regions
RegionManager regionManager = plugin.getRegionManager(event.getPlayer().getWorld());
// If the secondLine does not contain a name try to find the region by location
if(secondLine == null || secondLine.isEmpty()) {
Set<ProtectedRegion> regions = worldGuardInterface.getApplicableRegionsSet(event.getBlock().getLocation());
if(regions != null) {
boolean first = true;
ProtectedRegion candidate = null;
for(ProtectedRegion pr : regions) {
if(first) {
candidate = pr;
first = false;
} else {
if(pr.getPriority() > candidate.getPriority()) {
candidate = pr;
} else if(pr.getParent() != null && pr.getParent().equals(candidate)) {
candidate = pr;
} else {
messageBridge.message(player, "setup-couldNotDetect", candidate.getId(), pr.getId());
return;
}
}
}
if(candidate != null) {
secondLine = candidate.getId();
}
}
}
boolean priceSet = fourthLine != null && !fourthLine.isEmpty();
boolean durationSet = thirdLine != null && !thirdLine.isEmpty();
// check if all the lines are correct
if(secondLine == null || secondLine.isEmpty()) {
messageBridge.message(player, "setup-noRegion");
return;
}
ProtectedRegion region = regionManager.getRegion(secondLine);
if(region == null) {
messageBridge.message(player, "cmd-noRegion", secondLine);
return;
}
IFileManager.AddResult addResult = fileManager.checkRegionAdd(player, regionManager.getRegion(secondLine), event.getPlayer().getWorld(), GeneralRegion.RegionType.RENT);
if(addResult == IFileManager.AddResult.BLACKLISTED) {
messageBridge.message(player, "setup-blacklisted", secondLine);
} else if(addResult == IFileManager.AddResult.ALREADYADDED) {
messageBridge.message(player, "setup-alreadyRentSign");
} else if(addResult == IFileManager.AddResult.ALREADYADDEDOTHERWORLD) {
messageBridge.message(player, "setup-alreadyOtherWorld");
} else if(addResult == IFileManager.AddResult.NOPERMISSION) {
messageBridge.message(player, "setup-noPermission", secondLine);
} else if(thirdLine != null && !thirdLine.isEmpty() && !Utils.checkTimeFormat(thirdLine)) {
messageBridge.message(player, "setup-wrongDuration");
} else {
double price = 0.0;
if(priceSet) {
// Check the fourth line
try {
price = Double.parseDouble(fourthLine);
} catch(NumberFormatException e) {
messageBridge.message(player, "setup-wrongPrice");
return;
}
}
// Add rent to the FileManager
final RentRegion rent = regionFactory.createRentRegion(secondLine, event.getPlayer().getWorld());
boolean isMember = worldGuardInterface.containsMember(rent.getRegion(), player.getUniqueId());
boolean isOwner = worldGuardInterface.containsOwner(rent.getRegion(), player.getUniqueId());
boolean landlord = (!player.hasPermission("areashop.createrent")
&& ((player.hasPermission("areashop.createrent.owner") && isOwner)
|| (player.hasPermission("areashop.createrent.member") && isMember)));
if(landlord) {
rent.setLandlord(player.getUniqueId(), player.getName());
}
if(priceSet) {
rent.setPrice(price);
}
if(durationSet) {
rent.setDuration(thirdLine);
}
rent.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), SignUtils.getSignFacing(event.getBlock()), null);
AddingRegionEvent addingRegionEvent = plugin.getFileManager().addRegion(rent);
if (addingRegionEvent.isCancelled()) {
messageBridge.message(player, "general-cancelled", addingRegionEvent.getReason());
return;
}
rent.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
messageBridge.message(player, "setup-rentSuccess", rent);
// Update the region after the event has written its lines
Do.sync(rent::update);
}
} else if(event.getLine(0).contains(plugin.getConfig().getString("signTags.buy"))) {
// Check for permission
if(!player.hasPermission("areashop.createbuy") && !player.hasPermission("areashop.createbuy.member") && !player.hasPermission("areashop.createbuy.owner")) {
messageBridge.message(player, "setup-noPermissionBuy");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
// Get the regionManager for accessing regions
RegionManager regionManager = plugin.getRegionManager(event.getPlayer().getWorld());
// If the secondLine does not contain a name try to find the region by location
if(secondLine == null || secondLine.isEmpty()) {
Set<ProtectedRegion> regions = worldGuardInterface.getApplicableRegionsSet(event.getBlock().getLocation());
if(regions != null) {
boolean first = true;
ProtectedRegion candidate = null;
for(ProtectedRegion pr : regions) {
if(first) {
candidate = pr;
first = false;
} else {
if(pr.getPriority() > candidate.getPriority()) {
candidate = pr;
} else if(pr.getParent() != null && pr.getParent().equals(candidate)) {
candidate = pr;
} else {
messageBridge.message(player, "setup-couldNotDetect", candidate.getId(), pr.getId());
return;
}
}
}
if(candidate != null) {
secondLine = candidate.getId();
}
}
}
boolean priceSet = thirdLine != null && !thirdLine.isEmpty();
// Check if all the lines are correct
if(secondLine == null || secondLine.isEmpty()) {
messageBridge.message(player, "setup-noRegion");
return;
}
ProtectedRegion region = regionManager.getRegion(secondLine);
if(region == null) {
messageBridge.message(player, "cmd-noRegion", secondLine);
return;
}
IFileManager.AddResult addResult = plugin.getFileManager().checkRegionAdd(player, region, event.getPlayer().getWorld(), GeneralRegion.RegionType.BUY);
if(addResult == IFileManager.AddResult.BLACKLISTED) {
messageBridge.message(player, "setup-blacklisted", secondLine);
} else if(addResult == IFileManager.AddResult.ALREADYADDED) {
messageBridge.message(player, "setup-alreadyRentSign");
} else if(addResult == IFileManager.AddResult.ALREADYADDEDOTHERWORLD) {
messageBridge.message(player, "setup-alreadyOtherWorld");
} else if(addResult == IFileManager.AddResult.NOPERMISSION) {
messageBridge.message(player, "setup-noPermission", secondLine);
} else {
double price = 0.0;
if(priceSet) {
// Check the fourth line
try {
price = Double.parseDouble(thirdLine);
} catch(NumberFormatException e) {
messageBridge.message(player, "setup-wrongPrice");
return;
}
}
// Add buy to the FileManager
final BuyRegion buy = regionFactory.createBuyRegion(secondLine, event.getPlayer().getWorld());
boolean isMember = worldGuardInterface.containsMember(buy.getRegion(), player.getUniqueId());
boolean isOwner = worldGuardInterface.containsOwner(buy.getRegion(), player.getUniqueId());
boolean landlord = (!player.hasPermission("areashop.createbuy")
&& ((player.hasPermission("areashop.createbuy.owner") && isOwner)
|| (player.hasPermission("areashop.createbuy.member") && isMember)));
if(landlord) {
buy.setLandlord(player.getUniqueId(), player.getName());
}
if(priceSet) {
buy.setPrice(price);
}
buy.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), SignUtils.getSignFacing(event.getBlock()), null);
AddingRegionEvent addingRegionEvent = plugin.getFileManager().addRegion(buy);
if (addingRegionEvent.isCancelled()) {
messageBridge.message(player, "general-cancelled", addingRegionEvent.getReason());
return;
}
buy.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
messageBridge.message(player, "setup-buySuccess", buy);
// Update the region after the event has written its lines
Do.sync(buy::update);
}
} else if(event.getLine(0).contains(plugin.getConfig().getString("signTags.add"))) {
// Check for permission
if(!player.hasPermission("areashop.addsign")) {
messageBridge.message(player, "addsign-noPermission");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
GeneralRegion region;
if(secondLine != null && !secondLine.isEmpty()) {
// Get region by secondLine of the sign
region = fileManager.getRegion(secondLine);
if(region == null) {
messageBridge.message(player, "addSign-notRegistered", secondLine);
return;
}
} else {
// Get region by sign position
List<GeneralRegion> regions = Utils.getImportantRegions(event.getBlock().getLocation());
if(regions.isEmpty()) {
messageBridge.message(player, "addsign-noRegions");
return;
} else if(regions.size() > 1) {
messageBridge.message(player, "addsign-couldNotDetectSign", regions.get(0).getName(), regions.get(1).getName());
return;
}
region = regions.get(0);
}
if(thirdLine == null || thirdLine.isEmpty()) {
region.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), SignUtils.getSignFacing(event.getBlock()), null);
messageBridge.message(player, "addsign-success", region);
} else {
region.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), SignUtils.getSignFacing(event.getBlock()), thirdLine);
messageBridge.message(player, "addsign-successProfile", thirdLine, region);
}
// Update the region later because this event will do it first
Do.sync(region::update);
}
}
}
@@ -0,0 +1,110 @@
package me.wiefferink.areashop.features.signs;
import io.github.bakedlibs.dough.blocks.BlockPosition;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.managers.Manager;
import org.bukkit.Location;
import org.bukkit.World;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class SignManager extends Manager {
private final Map<World, SignCache> signCacheMap = new HashMap<>();
public Collection<BlockPosition> allSignLocations() {
final Set<BlockPosition> set = new HashSet<>(signCacheMap.size());
for (SignCache signCache : signCacheMap.values()) {
set.addAll(signCache.allSignLocations());
}
return set;
}
public Collection<RegionSign> allSigns() {
final Set<RegionSign> signs = new HashSet<>(signCacheMap.size());
for (SignCache signCache : signCacheMap.values()) {
signs.addAll(signCache.allSigns());
}
return signs;
}
public SignCache cacheForWorld(World world) {
return this.signCacheMap.computeIfAbsent(world, x -> new SignCache());
}
public Optional<SignCache> getCacheForWorld(World world) {
return Optional.ofNullable(this.signCacheMap.get(world));
}
public Optional<RegionSign> signFromLocation(Location location) {
final World world = location.getWorld();
return getCacheForWorld(world)
.flatMap(cache -> cache.signAtLocation(BlockPosition.getAsLong(location)));
}
public void addSign(RegionSign regionSign) {
Location location = regionSign.getLocation();
cacheForWorld(location.getWorld()).addSign(regionSign);
}
public void removeSign(RegionSign regionSign) {
Location location = regionSign.getLocation();
if (location.getWorld() == null) {
return;
}
cacheForWorld(location.getWorld()).removeSign(regionSign);
}
public Optional<RegionSign> removeSign(Location location) {
if (location == null || location.getWorld() == null) {
return Optional.empty();
}
return getCacheForWorld(location.getWorld()).flatMap(cache -> cache.removeSign(location));
}
/**
* Update all signs connected to this region.
* @return true if all signs are updated correctly, false if one or more updates failed
*/
public boolean update() {
boolean result = true;
for (SignCache signCache : this.signCacheMap.values())
for(RegionSign sign : signCache.allSigns()) {
result &= sign.update();
}
return result;
}
/**
* Check if any of the signs need periodic updating.
* @return true if one or more of the signs need periodic updating, otherwise false
*/
public boolean needsPeriodicUpdate() {
boolean result = false;
for (SignCache signCache : this.signCacheMap.values()) {
for (RegionSign sign : signCache.allSigns()) {
if (result) {
break;
}
if (sign.needsPeriodicUpdate()) {
result = true;
break;
}
}
}
return result;
}
public void shutdown() {
update();
this.signCacheMap.values().forEach(SignCache::clear);
this.signCacheMap.clear();
}
}
@@ -0,0 +1,108 @@
package me.wiefferink.areashop.features.signs;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import io.github.bakedlibs.dough.blocks.BlockPosition;
import io.github.bakedlibs.dough.blocks.ChunkPosition;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.RegionFeature;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.ConfigurationSection;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
public class SignsFeature extends RegionFeature {
private final SignManager internalSignManager = new SignManager();
private final SignManager globalSignManager;
private final SignFactory signFactory;
/**
* Constructor.
* @param region The region to bind to
*/
@AssistedInject
SignsFeature(@Nonnull AreaShop plugin,
@Nonnull SignManager signManager,
@Nonnull SignFactory signFactory,
@Assisted @Nonnull GeneralRegion region
) {
super(plugin);
this.globalSignManager = signManager;
this.signFactory = signFactory;
setRegion(region);
// Setup current signs
ConfigurationSection signSection = region.getConfig().getConfigurationSection("general.signs");
if (signSection == null) {
return;
}
for(String signKey : signSection.getKeys(false)) {
RegionSign sign = signFactory.createRegionSign(this, signKey);
Location location = sign.getLocation();
if(location == null) {
AreaShop.warn("Sign with key " + signKey + " of region " + region.getName() + " does not have a proper location");
continue;
}
this.globalSignManager.cacheForWorld(location.getWorld()).addSign(sign);
this.internalSignManager.addSign(sign);
}
}
/**
* Convert a location to a string to use as map key.
* @param location The location to get the key for
* @return A string to use in a map for a location
*/
public static String locationToString(Location location) {
return location.getWorld().getName() + ";" + location.getBlockX() + ";" + location.getBlockY() + ";" + location.getBlockZ();
}
/**
* Convert a chunk to a string to use as map key.
* @param location The location to get the key for
* @return A string to use in a map for a chunk
*/
public static String chunkToString(Location location) {
return location.getWorld().getName() + ";" + (location.getBlockX() >> 4) + ";" + (location.getBlockZ() >> 4);
}
@Override
public void shutdown() {
}
public SignManager signManager() {
return this.internalSignManager;
}
/**
* Add a sign to this region.
* @param location The location of the sign
* @param signType The type of the sign (WALL_SIGN or SIGN_POST)
* @param facing The orientation of the sign
* @param profile The profile to use with this sign (null for default)
*/
public void addSign(Location location, Material signType, BlockFace facing, String profile) {
int i = 0;
while(getRegion().getConfig().isSet("general.signs." + i)) {
i++;
}
String signPath = "general.signs." + i + ".";
getRegion().setSetting(signPath + "location", Utils.locationToConfig(location));
getRegion().setSetting(signPath + "facing", facing != null ? facing.name() : null);
getRegion().setSetting(signPath + "signType", signType != null ? signType.name() : null);
if(profile != null && !profile.isEmpty()) {
getRegion().setSetting(signPath + "profile", profile);
}
// Add to the map
RegionSign regionSign = this.signFactory.createRegionSign(this, String.valueOf(i));
this.globalSignManager.addSign(regionSign);
this.internalSignManager.addSign(regionSign);
}
}
@@ -0,0 +1,50 @@
package me.wiefferink.areashop.features.signs;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.managers.SignLinkerManager;
import me.wiefferink.areashop.nms.BlockBehaviourHelper;
import me.wiefferink.areashop.regions.RegionFactory;
import javax.annotation.Nonnull;
public class SignsModule extends AbstractModule {
@Override
protected void configure() {
bind(SignManager.class).asEagerSingleton();
install(new FactoryModuleBuilder().build(SignFactory.class));
}
@Provides
@Singleton
public SignListener provideSignListener(@Nonnull AreaShop plugin,
@Nonnull BlockBehaviourHelper behaviourHelper,
@Nonnull RegionFactory regionFactory,
@Nonnull MessageBridge messageBridge,
@Nonnull SignLinkerManager signLinkerManager,
@Nonnull WorldGuardInterface worldGuardInterface,
@Nonnull SignManager signManager,
@Nonnull IFileManager fileManager
) {
final SignListener signListener = new SignListener(
plugin,
behaviourHelper,
regionFactory,
messageBridge,
signLinkerManager,
worldGuardInterface,
signManager,
fileManager
);
plugin.getServer().getPluginManager().registerEvents(signListener, plugin);
return signListener;
}
}
@@ -0,0 +1,168 @@
package me.wiefferink.areashop.listeners;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.bukkitdo.Do;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerQuitEvent;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
/**
* Notify region expiry and track activity time.
*/
public final class PlayerLoginLogoutListener implements Listener {
private final AreaShop plugin;
private final MessageBridge messageBridge;
/**
* Constructor.
* @param plugin The AreaShop plugin
*/
public PlayerLoginLogoutListener(@Nonnull AreaShop plugin, @Nonnull MessageBridge messageBridge) {
this.plugin = plugin;
this.messageBridge = messageBridge;
}
/**
* Called when a sign is changed.
* @param event The event
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLogin(PlayerLoginEvent event) {
if(event.getResult() != Result.ALLOWED) {
return;
}
final Player player = event.getPlayer();
// Schedule task to check for notifications, prevents a lag spike at login
Do.syncTimerLater(25, 25, () -> {
// Delay until all regions are loaded
if(!plugin.isReady()) {
return true;
}
if(!player.isOnline()) {
return false;
}
// Notify for rents that almost run out
for(RentRegion region : plugin.getFileManager().getRentsRef()) {
if(region.isRenter(player)) {
String warningSetting = region.getStringSetting("rent.warningOnLoginTime");
if(warningSetting == null || warningSetting.isEmpty()) {
continue;
}
long warningTime = Utils.durationStringToLong(warningSetting);
if(region.getTimeLeft() < warningTime) {
// Send the warning message later to let it appear after general MOTD messages
messageBridge.message(player, "rent-expireWarning", region);
}
}
}
// Notify admins for plugin updates
this.plugin.notifyUpdate(player);
return false;
});
// Check if the player has regions that use an old name of him and update them
Do.syncTimerLater(22, 10, () -> {
if(!plugin.isReady()) {
return true;
}
List<GeneralRegion> regions = new ArrayList<>();
for(GeneralRegion region : plugin.getFileManager().getRegionsRef()) {
if(region.isOwner(player)) {
regions.add(region);
}
}
Do.forAll(
plugin.getConfig().getInt("nameupdate.regionsPerTick"),
regions,
region -> {
if(region instanceof BuyRegion) {
if(!player.getName().equals(region.getStringSetting("buy.buyerName"))) {
region.setSetting("buy.buyerName", player.getName());
region.update();
}
} else if(region instanceof RentRegion) {
if(!player.getName().equals(region.getStringSetting("rent.renterName"))) {
region.setSetting("rent.renterName", player.getName());
region.update();
}
}
}
);
return false;
});
}
// Active time updates
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLogout(PlayerQuitEvent event) {
updateLastActive(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerKick(PlayerKickEvent event) {
updateLastActive(event.getPlayer());
}
/**
* Update the last active time for all regions the player is owner off.
* @param player The player to update the active times for
*/
private void updateLastActive(Player player) {
for(GeneralRegion region : plugin.getFileManager().getRegionsRef()) {
if(region.isOwner(player)) {
region.updateLastActiveTime();
}
}
}
}
@@ -0,0 +1,82 @@
package me.wiefferink.areashop.managers;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class AdventureLanguageManager {
private final Map<String, LanguageRegistry> languageRegistryMap = new HashMap<>();
private String currentLanguage;
private String fallbackLanguage;
private String chatPrefix = "";
public AdventureLanguageManager(String currentLanguage, String fallbackLanguage) {
this.currentLanguage = currentLanguage;
this.fallbackLanguage = fallbackLanguage;
}
public AdventureLanguageManager currentLanguage(@NotNull String language) {
this.currentLanguage = language;
return this;
}
public AdventureLanguageManager fallbackLanguage(@NotNull String language) {
this.fallbackLanguage = language;
return this;
}
public AdventureLanguageManager loadValues(@NotNull String language,
@NotNull Map<String, String> values) {
this.languageRegistryMap.put(language, new LanguageRegistry(values));
return this;
}
public String messageFor(String key, String lang) {
LanguageRegistry registry = this.languageRegistryMap.get(lang);
if (registry == null) {
return "";
}
return registry.messageFor(key);
}
public String messageFor(String key) {
String message = messageFor(key, this.currentLanguage);
if (message.isEmpty() && !this.currentLanguage.equals(fallbackLanguage)) {
message = messageFor(key, this.fallbackLanguage);
}
return message;
}
public void chatPrefix(String prefix) {
this.chatPrefix = prefix;
}
public String chatPrefix() {
return this.chatPrefix;
}
private static class LanguageRegistry {
private final Map<String, String> messages = new HashMap<>();
public LanguageRegistry(Map<String, String> messages) {
this.messages.putAll(messages);
}
public void messageFor(String key, String message) {
this.messages.put(key, message);
}
public String messageFor(String key) {
return this.messages.getOrDefault(key, "");
}
public boolean isEmpty() {
return this.messages.isEmpty();
}
}
}
@@ -0,0 +1,234 @@
package me.wiefferink.areashop.managers;
import com.google.inject.Injector;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.commands.AddCommand;
import me.wiefferink.areashop.commands.AddfriendCommand;
import me.wiefferink.areashop.commands.AddsignCommand;
import me.wiefferink.areashop.commands.BuyCommand;
import me.wiefferink.areashop.commands.CommandAreaShop;
import me.wiefferink.areashop.commands.DelCommand;
import me.wiefferink.areashop.commands.DelfriendCommand;
import me.wiefferink.areashop.commands.DelsignCommand;
import me.wiefferink.areashop.commands.FindCommand;
import me.wiefferink.areashop.commands.GroupaddCommand;
import me.wiefferink.areashop.commands.GroupdelCommand;
import me.wiefferink.areashop.commands.GroupinfoCommand;
import me.wiefferink.areashop.commands.GrouplistCommand;
import me.wiefferink.areashop.commands.HelpCommand;
import me.wiefferink.areashop.commands.ImportCommand;
import me.wiefferink.areashop.commands.InfoCommand;
import me.wiefferink.areashop.commands.LinksignsCommand;
import me.wiefferink.areashop.commands.MeCommand;
import me.wiefferink.areashop.commands.MessageCommand;
import me.wiefferink.areashop.commands.ReloadCommand;
import me.wiefferink.areashop.commands.RentCommand;
import me.wiefferink.areashop.commands.ResellCommand;
import me.wiefferink.areashop.commands.SchematiceventCommand;
import me.wiefferink.areashop.commands.SellCommand;
import me.wiefferink.areashop.commands.SetdurationCommand;
import me.wiefferink.areashop.commands.SetlandlordCommand;
import me.wiefferink.areashop.commands.SetownerCommand;
import me.wiefferink.areashop.commands.SetpriceCommand;
import me.wiefferink.areashop.commands.SetrestoreCommand;
import me.wiefferink.areashop.commands.SetteleportCommand;
import me.wiefferink.areashop.commands.StackCommand;
import me.wiefferink.areashop.commands.StopresellCommand;
import me.wiefferink.areashop.commands.TeleportCommand;
import me.wiefferink.areashop.commands.UnrentCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
@Singleton
public class CommandManager extends Manager implements CommandExecutor, TabCompleter {
private final List<CommandAreaShop> commands;
private final AreaShop plugin;
private final MessageBridge messageBridge;
/**
* Constructor.
*/
@Inject
CommandManager(@Nonnull AreaShop plugin, @Nonnull MessageBridge messageBridge, @Nonnull
Injector injector) {
this.plugin = plugin;
this.messageBridge = messageBridge;
commands = new ArrayList<>(34);
commands.add(new HelpCommand(this));
commands.add(injector.getInstance(RentCommand.class));
commands.add(injector.getInstance(UnrentCommand.class));
commands.add(injector.getInstance(BuyCommand.class));
commands.add(injector.getInstance(SellCommand.class));
commands.add(injector.getInstance(MeCommand.class));
commands.add(injector.getInstance(InfoCommand.class));
commands.add(injector.getInstance(TeleportCommand.class));
commands.add(injector.getInstance(SetteleportCommand.class));
commands.add(injector.getInstance(AddfriendCommand.class));
commands.add(injector.getInstance(DelfriendCommand.class));
commands.add(injector.getInstance(FindCommand.class));
commands.add(injector.getInstance(ResellCommand.class));
commands.add(injector.getInstance(StopresellCommand.class));
commands.add(injector.getInstance(SetrestoreCommand.class));
commands.add(injector.getInstance(SetpriceCommand.class));
commands.add(injector.getInstance(SetownerCommand.class));
commands.add(injector.getInstance(SetdurationCommand.class));
commands.add(injector.getInstance(ReloadCommand.class));
commands.add(injector.getInstance(GroupaddCommand.class));
commands.add(injector.getInstance(GroupdelCommand.class));
commands.add(injector.getInstance(GrouplistCommand.class));
commands.add(injector.getInstance(GroupinfoCommand.class));
commands.add(injector.getInstance(SchematiceventCommand.class));
commands.add(injector.getInstance(AddCommand.class));
commands.add(injector.getInstance(DelCommand.class));
commands.add(injector.getInstance(AddsignCommand.class));
commands.add(injector.getInstance(DelsignCommand.class));
commands.add(injector.getInstance(LinksignsCommand.class));
commands.add(injector.getInstance(StackCommand.class));
commands.add(injector.getInstance(SetlandlordCommand.class));
commands.add(injector.getInstance(MessageCommand.class));
commands.add(injector.getInstance(ImportCommand.class));
// Register commands in bukkit
plugin.getCommand("AreaShop").setExecutor(this);
plugin.getCommand("AreaShop").setTabCompleter(this);
}
/**
* Get the list with AreaShop commands.
* @return The list with AreaShop commands
*/
public List<CommandAreaShop> getCommands() {
return commands;
}
/**
* Shows the help page for the CommandSender.
* @param target The CommandSender to show the help to
*/
public void showHelp(CommandSender target) {
if(!target.hasPermission("areashop.help")) {
messageBridge.message(target, "help-noPermission");
return;
}
// Add all messages to a list
ArrayList<String> messages = new ArrayList<>();
messageBridge.message(target, "help-header");
messageBridge.message(target, "help-alias");
for(CommandAreaShop command : commands) {
String help = command.getHelp(target);
if(help != null && !help.isEmpty()) {
messages.add(help);
}
}
// Send the messages to the target
for(String message : messages) {
messageBridge.messageNoPrefix(target, message);
}
}
@Override
public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) {
if(!plugin.isReady()) {
messageBridge.message(sender, "general-notReady");
return true;
}
// Redirect `/as info player <player>` to `/as me <player>`
if(args.length == 3 && "info".equals(args[0]) && "player".equals(args[1])) {
args[0] = "me";
args[1] = args[2];
}
// Execute command
boolean executed = false;
for(int i = 0; i < commands.size() && !executed; i++) {
if(commands.get(i).canExecute(command, args)) {
commands.get(i).execute(sender, args);
executed = true;
}
}
// Show help
if (!executed) {
if (args.length == 0) {
this.showHelp(sender);
} else {
// Indicate that the '/as updaterents' and '/as updatebuys' commands are removed
if("updaterents".equalsIgnoreCase(args[0]) || "updatebuys".equalsIgnoreCase(args[0])) {
messageBridge.message(sender, "reload-updateCommandChanged");
} else {
messageBridge.message(sender, "cmd-notValid");
}
}
}
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List<String> result = new ArrayList<>();
if(!sender.hasPermission("areashop.tabcomplete")) {
return result;
}
int toCompleteNumber = args.length;
String toCompletePrefix = args[args.length - 1].toLowerCase();
//AreaShop.debug("toCompleteNumber=" + toCompleteNumber + ", toCompletePrefix=" + toCompletePrefix + ", length=" + toCompletePrefix.length());
if(toCompleteNumber == 1) {
for(CommandAreaShop c : commands) {
String begin = c.getCommandStart();
result.add(begin.substring(begin.indexOf(' ') + 1));
}
} else {
String[] start = new String[args.length];
start[0] = command.getName();
System.arraycopy(args, 0, start, 1, args.length - 1);
for(CommandAreaShop c : commands) {
if(c.canExecute(command, args)) {
result = c.getTabCompleteList(toCompleteNumber, start, sender);
}
}
}
// Filter and sort the results
if(!result.isEmpty()) {
SortedSet<String> set = new TreeSet<>();
for(String suggestion : result) {
if(suggestion.toLowerCase().startsWith(toCompletePrefix)) {
set.add(suggestion);
}
}
result.clear();
result.addAll(set);
}
//AreaShop.debug("Tabcomplete #" + toCompleteNumber + ", prefix="+ toCompletePrefix + ", result=" + result.toString());
return result;
}
}
@@ -0,0 +1,92 @@
package me.wiefferink.areashop.managers;
import com.google.inject.Injector;
import com.google.inject.ProvisionException;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.CommandsFeature;
import me.wiefferink.areashop.features.DebugFeature;
import me.wiefferink.areashop.features.FeatureFactory;
import me.wiefferink.areashop.features.FriendsFeature;
import me.wiefferink.areashop.features.RegionFeature;
import me.wiefferink.areashop.features.TeleportFeature;
import me.wiefferink.areashop.features.WorldGuardRegionFlagsFeature;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.regions.GeneralRegion;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
@Singleton
public class FeatureManager extends Manager {
private static final Collection<Class<? extends RegionFeature>> globalFeatureClasses = Arrays.asList(
CommandsFeature.class,
WorldGuardRegionFlagsFeature.class,
DebugFeature.class
);
// One instance of each feature, registered for event handling
private final Set<RegionFeature> globalFeatures = new HashSet<>();
private final Map<Class<? extends RegionFeature>, Function<GeneralRegion, ? extends RegionFeature>> regionFeatureConstructors = new HashMap<>();
/**
* Constructor.
*/
@Inject
FeatureManager(@Nonnull FeatureFactory featureFactory) {
// Setup constructors for region specific features
regionFeatureConstructors.put(SignsFeature.class, wrapInstantiator(SignsFeature.class, featureFactory::createSignsFeature));
regionFeatureConstructors.put(TeleportFeature.class, wrapInstantiator(TeleportFeature.class, featureFactory::createTeleportFeature));
regionFeatureConstructors.put(FriendsFeature.class, wrapInstantiator(FriendsFeature.class, featureFactory::createFriendsFeature));
}
public void initializeFeatures(@Nonnull Injector injector) {
// Instantiate and register global features (one per type, for event handling)
for(Class<? extends RegionFeature> clazz : globalFeatureClasses) {
try {
RegionFeature feature = injector.getInstance(clazz);
feature.listen();
globalFeatures.add(feature);
} catch(ProvisionException e) {
AreaShop.error("Failed to instantiate global feature:", clazz, e);
}
}
}
private <T extends RegionFeature> Function<GeneralRegion, T> wrapInstantiator(Class<T> clazz, Function<GeneralRegion, T> function
) {
return region -> {
try {
return function.apply(region);
} catch (Throwable e) {
AreaShop.error("Failed to instantiate feature", clazz, "for region", region, e, e.getCause());
return null;
}
};
}
@Override
public void shutdown() {
for(RegionFeature feature : globalFeatures) {
feature.shutdown();
}
}
/**
* Instanciate a feature for a certain region.
* @param region The region to create a feature for
* @param featureClazz The class of the feature to create
* @return The feature class
*/
public RegionFeature getRegionFeature(GeneralRegion region, Class<? extends RegionFeature> featureClazz) {
return regionFeatureConstructors.get(featureClazz).apply(region);
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,399 @@
package me.wiefferink.areashop.managers;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.events.ask.DeletingRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.regions.RentRegion;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
public interface IFileManager {
/**
* Get the folder where schematics are stored.
*
* @return The folder where schematics are stored
*/
String getSchematicFolder();
/**
* Get a group.
*
* @param name The name of the group to get (will be normalized)
* @return The group if found, otherwise null
*/
RegionGroup getGroup(String name);
/**
* Get all groups.
*
* @return Collection with all groups (safe to modify)
*/
Collection<RegionGroup> getGroups();
/**
* Get the default region settings as provided by the user (default.yml).
*
* @return YamlConfiguration with the settings (might miss settings, which should be filled in with {@link #getFallbackRegionSettings()})
*/
YamlConfiguration getRegionSettings();
/**
* Get the default regions settings as provided by AreaShop (default.yml).
*
* @return YamlConfiguration with the default settings
*/
YamlConfiguration getFallbackRegionSettings();
/**
* Get the config file (config.yml).
*
* @return YamlConfiguration with the settings of users, with fallback to the settings provided by AreaShop
*/
YamlConfiguration getConfig();
/**
* Get a region.
*
* @param name The name of the region to get (will be normalized)
* @return The region if found, otherwise null
*/
@Nullable
GeneralRegion getRegion(String name);
/**
* Get a rental region.
*
* @param name The name of the rental region (will be normalized)
* @return RentRegion if it could be found, otherwise null
*/
@Nullable
RentRegion getRent(String name);
/**
* Get a buy region.
*
* @param name The name of the buy region (will be normalized)
* @return BuyRegion if it could be found, otherwise null
*/
@Nullable
BuyRegion getBuy(String name);
/**
* Get all rental regions.
*
* @return List of all rental regions
*/
Collection<RentRegion> getRents();
/**
* Get all rental regions (readonly)
*
* @return Unmodifiable collection of all rental regions
*/
Collection<RentRegion> getRentsRef();
/**
* Get all buy regions.
*
* @return List of all buy regions
*/
List<BuyRegion> getBuys();
/**
* Get all buy regions (readonly)
* @return Unmodifiable collection of all buy regions
*/
Collection<BuyRegion> getBuysRef();
/**
* Get all regions.
*
* @return List of all regions (it is safe to modify the list)
*/
List<GeneralRegion> getRegions();
/**
* Get all regions (readonly)
*
* @return Unmodifiable collection of all regions
*/
Collection<GeneralRegion> getRegionsRef();
/**
* Get a list of names of all buy regions.
*
* @return A String list with all the names
*/
List<String> getBuyNames();
/**
* Get a list of names of all rent regions.
*
* @return A String list with all the names
*/
List<String> getRentNames();
/**
* Get a list of names of all regions.
*
* @return A String list with all the names
*/
List<String> getRegionNames();
/**
* Get a list of names of all groups.
*
* @return A String list with all the names
*/
List<String> getGroupNames();
/**
* Add a region to the list and mark it as to-be-saved.
*
* @param region Then region to add
* @return true when successful, otherwise false (denied by an event listener)
*/
AddingRegionEvent addRegion(GeneralRegion region);
/**
* Add a region to the list without saving it to disk (useful for loading at startup).
*
* @param region The region to add
* @return true when successful, otherwise false (denied by an event listener)
*/
AddingRegionEvent addRegionNoSave(GeneralRegion region);
/**
* Mark all RegionGroups that they should regenerate regions.
*/
void markGroupsAutoDirty();
/**
* Add a RegionGroup.
*
* @param group The RegionGroup to add
*/
void addGroup(RegionGroup group);
/**
* Check if a player can add a certain region as rent or buy region.
*
* @param sender The player/console that wants to add a region
* @param region The WorldGuard region to add
* @param world The world the ProtectedRegion is located in
* @param type The type the region should have in AreaShop
* @return The result if a player would want to add this region
*/
AddResult checkRegionAdd(CommandSender sender,
ProtectedRegion region,
World world,
GeneralRegion.RegionType type);
/**
* Remove a region from the list.
*
* @param region The region to remove
* @param giveMoneyBack use true to give money back to the player if someone is currently holding this region, otherwise false
* @return true if the region has been removed, false otherwise
*/
DeletingRegionEvent deleteRegion(GeneralRegion region, boolean giveMoneyBack);
/**
* Remove a group.
*
* @param group Group to remove
*/
void removeGroup(RegionGroup group);
/**
* Update all signs that need periodic updating.
*/
void performPeriodicSignUpdate();
/**
* Send out rent expire warnings.
*/
void sendRentExpireWarnings();
/**
* Update regions in a task to minimize lag.
*
* @param regions Regions to update
* @param confirmationReceiver The CommandSender that should be notified at completion
*/
void updateRegions(Collection<GeneralRegion> regions, CommandSender confirmationReceiver);
/**
* Update a list of regions.
*
* @param regions The list of regions to update.
*/
void updateRegions(Collection<GeneralRegion> regions);
/**
* Update all regions, happens in a task to minimize lag.
*/
void updateAllRegions();
/**
* Update all regions.
*
* @param confirmationReceiver Optional CommandSender that should receive progress messages
*/
void updateAllRegions(CommandSender confirmationReceiver);
/**
* Save the group file to disk.
*/
void saveGroupsIsRequired();
/**
* Check if saving the groups file is required.
*
* @return true if changes are made and saving is required, otherwise false
*/
boolean isSaveGroupsRequired();
/**
* Save the groups file to disk synchronously.
*/
void saveGroupsNow();
/**
* Save all region related files spread over time (low load).
*/
void saveRequiredFiles();
/**
* Save all region related files directly (only for cases like onDisable()).
*/
void saveRequiredFilesAtOnce();
/**
* Indicates that a/multiple WorldGuard regions need to be saved.
*
* @param worldName The world where the regions that should be saved is in
*/
void saveIsRequiredForRegionWorld(String worldName);
/**
* Save all worldGuard regions that need saving.
*/
void saveWorldGuardRegions();
/**
* Get the folder the region files are located in.
*
* @return The folder where the region.yml files are in
*/
String getRegionFolder();
/**
* Check if a region is on the adding blacklist.
*
* @param region The region name to check
* @return true if the region may not be added, otherwise false
*/
boolean isBlacklisted(String region);
/**
* Unrent regions that have no time left, regions to check per tick is in the config.
*/
void checkRents();
/**
* Check all regions and unrent/sell them if the player is inactive for too long.
*/
void checkForInactiveRegions();
/**
* Load the file with the versions, used to check if the other files need conversion.
*/
@SuppressWarnings("unchecked")
void loadVersions();
/**
* Save the versions file to disk.
*/
void saveVersions();
/**
* Load all files from disk.
*
* @param thisTick Load files in the current tick or a tick later
* @return true if the files are loaded correctly, otherwise false
*/
boolean loadFiles(boolean thisTick);
/**
* Load the default.yml file
*
* @return true if it has been loaded successfully, otherwise false
*/
boolean loadDefaultFile();
/**
* Load the default.yml file
*
* @return true if it has been loaded successfully, otherwise false
*/
boolean loadConfigFile();
/**
* Load the groups.yml file from disk
*
* @return true if succeeded, otherwise false
*/
boolean loadGroupsFile();
/**
* Load all region files.
*/
void loadRegionFiles();
/**
* Get the settings of a group.
*
* @param groupName Name of the group to get the settings from
* @return The settings of the group
*/
ConfigurationSection getGroupSettings(String groupName);
/**
* Set a setting for a group.
*
* @param group The group to set it for
* @param path The path to set
* @param setting The value to set
*/
void setGroupSetting(RegionGroup group, String path, Object setting);
// Enum for region types
public enum AddResult {
BLACKLISTED("blacklisted"),
NOPERMISSION("nopermission"),
ALREADYADDED("alreadyadded"),
ALREADYADDEDOTHERWORLD("alreadyaddedotherworld"),
SUCCESS("success");
private final String value;
AddResult(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}
@@ -0,0 +1,12 @@
package me.wiefferink.areashop.managers;
public abstract class Manager {
/**
* Called at shutdown of the plugin.
*/
public void shutdown() {
// To override by extending classes
}
}
@@ -0,0 +1,79 @@
package me.wiefferink.areashop.managers;
import me.wiefferink.areashop.AreaShop;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicBoolean;
public class SignErrorLogger {
public static final String PREFIX_WARN = "[Warning] [Sign Feature] ";
public static final DateFormat FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm:ss");
private final List<String> cache = new LinkedList<>();
private final AtomicBoolean needsSaving = new AtomicBoolean(false);
private final File file;
public SignErrorLogger(File file) {
this.file = Objects.requireNonNull(file);
}
public void clearCache() {
synchronized (this.cache) {
this.cache.clear();
}
}
public SignErrorLogger submitWarning(String message) {
final String actual = FORMAT.format(Date.from(Instant.now())) + PREFIX_WARN + message;
synchronized (this.cache) {
this.cache.add(actual);
}
return this;
}
public void queueSave() {
this.needsSaving.set(true);
}
public void saveIfDirty() {
if (this.needsSaving.get()) {
save();
}
}
public synchronized void save() {
AreaShop.debugTask("[Sign Feature] Dumping sign errors to disk...");
final String[] copy;
synchronized (this.cache) {
copy = cache.toArray(new String[0]);
}
final StringJoiner joiner = new StringJoiner(System.lineSeparator());
for (String s : copy) {
joiner.add(s);
}
try (OutputStream os = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
writer.write(joiner.toString());
} catch (IOException ex) {
ex.printStackTrace();
AreaShop.error("Failed to update the sign error log!");
} finally {
AreaShop.debugTask("[Sign Feature] Error dump complete.");
}
}
}
@@ -1,226 +1,245 @@
package nl.evolutioncoding.areashop.managers;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import nl.evolutioncoding.areashop.AreaShop;
import nl.evolutioncoding.areashop.Utils;
import nl.evolutioncoding.areashop.regions.GeneralRegion;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.material.Sign;
import org.bukkit.util.BlockIterator;
public class SignLinkerManager implements Listener {
private AreaShop plugin = null;
private Map<UUID, SignLinker> signLinkers;
private boolean eventsRegistered;
public SignLinkerManager(AreaShop plugin) {
this.plugin = plugin;
signLinkers = new HashMap<UUID, SignLinker>();
eventsRegistered = false;
}
/**
* Let a player enter sign linking mode
* @param player The player that has to enter sign linking mode
* @param profile The profile to use for the signs (null for default)
*/
public void enterSignLinkMode(Player player, String profile) {
signLinkers.put(player.getUniqueId(), new SignLinker(player, profile));
plugin.message(player, "linksigns-first");
plugin.message(player, "linksigns-next");
if(!eventsRegistered) {
eventsRegistered = true;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
}
/**
* Let a player exit sign linking mode
* @param player The player that has to exit sign linking mode
*/
public void exitSignLinkMode(Player player) {
signLinkers.remove(player.getUniqueId());
if(eventsRegistered && signLinkers.isEmpty()) {
eventsRegistered = false;
HandlerList.unregisterAll(this);
}
plugin.message(player, "linksigns-stopped");
}
/**
* Check if the player is in sign linking mode
* @param player The player to check
* @return true if the player is in sign linking mode, otherwise false
*/
public boolean isInSignLinkMode(Player player) {
return signLinkers.containsKey(player.getUniqueId());
}
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerInteract(PlayerInteractEvent event) {
AreaShop.debug("PlayerInteractEvent of " + event.getPlayer().getName() + ", " + signLinkers.size() + " signlinkers");
if(isInSignLinkMode(event.getPlayer())) {
event.setCancelled(true);
Player player = event.getPlayer();
SignLinker linker = signLinkers.get(event.getPlayer().getUniqueId());
if(event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
// Get the region
BlockIterator blockIterator = new BlockIterator(player, 100);
while(blockIterator.hasNext()) {
Block next = blockIterator.next();
List<GeneralRegion> regions = plugin.getFileManager().getASRegionsByLocation(next.getLocation());
if(regions.size() == 1) {
linker.setRegion(regions.get(0));
return;
} else if(regions.size() > 1) {
Set<String> names = new HashSet<String>();
for(GeneralRegion region : regions) {
names.add(region.getName());
}
plugin.message(player, "linksigns-multipleRegions", Utils.createCommaSeparatedList(names));
plugin.message(player, "linksigns-multipleRegionsAdvice");
return;
}
}
// No regions found within the maximum range
plugin.message(player, "linksigns-noRegions");
return;
} else if(event.getAction() == Action.LEFT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_BLOCK) {
Block block = null;
BlockIterator blockIterator = new BlockIterator(player, 100);
while(blockIterator.hasNext() && block == null) {
Block next = blockIterator.next();
if(next.getType() != Material.AIR) {
block = next;
}
}
if(block == null || !(block.getType() == Material.WALL_SIGN || block.getType() == Material.SIGN_POST)) {
plugin.message(player, "linksigns-noSign");
return;
}
GeneralRegion signRegion = plugin.getFileManager().getRegionBySignLocation(block.getLocation());
if(signRegion != null) {
plugin.message(player, "linksigns-alreadyRegistered", signRegion);
return;
}
Sign sign = (Sign)block.getState().getData();
linker.setSign(block.getLocation(), block.getType(), sign.getFacing());
return;
}
}
}
/**
* Handle disconnection players
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLeave(PlayerQuitEvent event) {
AreaShop.debug("PlayerQuitEvent of " + event.getPlayer().getName() + ", " + signLinkers.size() + " signlinkers");
signLinkers.remove(event.getPlayer().getUniqueId());
}
/**
* Class to keep track of the signlinking data
*/
private class SignLinker {
private boolean hasSign = false;
private boolean hasRegion = false;
public Player linker = null;
public String profile = null;
public GeneralRegion region = null;
public Location location = null;
public Material type = null;
public BlockFace facing = null;
public SignLinker(Player linker, String profile) {
this.linker = linker;
this.profile = profile;
}
public void setRegion(GeneralRegion region) {
this.region = region;
hasRegion = true;
if(!isComplete()) {
plugin.message(linker, "linksigns-regionFound", region);
}
finalize();
}
public void setSign(Location location, Material type, BlockFace facing) {
this.location = location;
this.type = type;
this.facing = facing;
hasSign = true;
if(!isComplete()) {
plugin.message(linker, "linksigns-signFound", location.getBlockX(), location.getBlockY(), location.getBlockZ());
}
finalize();
}
public void finalize() {
if(isComplete()) {
region.addSign(location, type, facing, profile);
if(profile == null) {
plugin.message(linker, "addsign-success", region);
} else {
plugin.message(linker, "addsign-successProfile", region, profile);
}
region.updateSigns();
region.saveRequired();
reset();
plugin.message(linker, "linksigns-next");
}
}
public void reset() {
hasSign = false;
hasRegion = false;
}
public boolean isComplete() {
return hasSign && hasRegion;
}
}
}
package me.wiefferink.areashop.managers;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignManager;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.SignUtils;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.util.BlockIterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@Singleton
public class SignLinkerManager extends Manager implements Listener {
private final Map<UUID, SignLinker> signLinkers;
private boolean eventsRegistered;
@Inject
private AreaShop plugin;
@Inject
private SignManager signManager;
@Inject
private MessageBridge messageBridge;
SignLinkerManager() {
signLinkers = new HashMap<>();
eventsRegistered = false;
}
@Override
public void shutdown() {
for(UUID uuid : signLinkers.keySet()) {
exitSignLinkMode(Bukkit.getPlayer(uuid));
}
}
/**
* Let a player enter sign linking mode.
* @param player The player that has to enter sign linking mode
* @param profile The profile to use for the signs (null for default)
*/
public void enterSignLinkMode(Player player, String profile) {
signLinkers.put(player.getUniqueId(), new SignLinker(player, profile));
messageBridge.message(player, "linksigns-first");
messageBridge.message(player, "linksigns-next");
if(!eventsRegistered) {
eventsRegistered = true;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
}
/**
* Let a player exit sign linking mode.
* @param player The player that has to exit sign linking mode
*/
public void exitSignLinkMode(Player player) {
signLinkers.remove(player.getUniqueId());
if(eventsRegistered && signLinkers.isEmpty()) {
eventsRegistered = false;
HandlerList.unregisterAll(this);
}
messageBridge.message(player, "linksigns-stopped");
}
/**
* Check if the player is in sign linking mode.
* @param player The player to check
* @return true if the player is in sign linking mode, otherwise false
*/
public boolean isInSignLinkMode(Player player) {
return signLinkers.containsKey(player.getUniqueId());
}
/**
* On player interactions.
* @param event The PlayerInteractEvent
*/
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerInteract(PlayerInteractEvent event) {
if(isInSignLinkMode(event.getPlayer())) {
event.setCancelled(true);
Player player = event.getPlayer();
SignLinker linker = signLinkers.get(event.getPlayer().getUniqueId());
if(event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
// Get the region
BlockIterator blockIterator = new BlockIterator(player, 100);
while(blockIterator.hasNext()) {
Block next = blockIterator.next();
List<GeneralRegion> regions = Utils.getRegions(next.getLocation());
if(regions.size() == 1) {
linker.setRegion(regions.get(0));
return;
} else if(regions.size() > 1) {
Set<String> names = new HashSet<>();
for(GeneralRegion region : regions) {
names.add(region.getName());
}
messageBridge.message(player, "linksigns-multipleRegions", Utils.createCommaSeparatedList(names));
messageBridge.message(player, "linksigns-multipleRegionsAdvice");
return;
}
}
// No regions found within the maximum range
messageBridge.message(player, "linksigns-noRegions");
} else if(event.getAction() == Action.LEFT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_BLOCK) {
Block block = null;
BlockIterator blockIterator = new BlockIterator(player, 100);
while(blockIterator.hasNext() && block == null) {
Block next = blockIterator.next();
if(next.getType() != Material.AIR) {
block = next;
}
}
if(block == null || !Materials.isSign(block.getType())) {
messageBridge.message(player, "linksigns-noSign");
return;
}
Optional<RegionSign> optional = signManager.signFromLocation(block.getLocation());
if(optional.isPresent()) {
RegionSign regionSign = optional.get();
messageBridge.message(player, "linksigns-alreadyRegistered", regionSign.getRegion());
return;
}
linker.setSign(block.getLocation(), block.getType(), SignUtils.getSignFacing(block));
}
}
}
/**
* Handle disconnection players.
* @param event The PlayerQuitEvent
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLeave(PlayerQuitEvent event) {
exitSignLinkMode(event.getPlayer());
}
/**
* Class to keep track of the signlinking data.
*/
private class SignLinker {
private boolean hasSign = false;
private boolean hasRegion = false;
public final Player linker;
public final String profile;
public GeneralRegion region = null;
public Location location = null;
public Material type = null;
public BlockFace facing = null;
public SignLinker(Player linker, String profile) {
this.linker = linker;
this.profile = profile;
}
public void setRegion(GeneralRegion region) {
this.region = region;
hasRegion = true;
if(!isComplete()) {
messageBridge.message(linker, "linksigns-regionFound", region);
}
finish();
}
public void setSign(Location location, Material type, BlockFace facing) {
this.location = location;
this.type = type;
this.facing = facing;
hasSign = true;
if(!isComplete()) {
messageBridge.message(linker, "linksigns-signFound", location.getBlockX(), location.getBlockY(), location.getBlockZ());
}
finish();
}
public void finish() {
if(isComplete()) {
region.getSignsFeature().addSign(location, type, facing, profile);
if(profile == null) {
messageBridge.message(linker, "addsign-success", region);
} else {
messageBridge.message(linker, "addsign-successProfile", region, profile);
}
region.update();
reset();
messageBridge.message(linker, "linksigns-next");
}
}
public void reset() {
hasSign = false;
hasRegion = false;
}
public boolean isComplete() {
return hasSign && hasRegion;
}
}
}
@@ -0,0 +1,78 @@
package me.wiefferink.areashop.modules;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.MessageBridge;
import me.wiefferink.areashop.features.FeatureFactory;
import me.wiefferink.areashop.features.signs.SignsModule;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.managers.AdventureLanguageManager;
import me.wiefferink.areashop.managers.CommandManager;
import me.wiefferink.areashop.managers.FeatureManager;
import me.wiefferink.areashop.managers.FileManager;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.managers.SignErrorLogger;
import me.wiefferink.areashop.managers.SignLinkerManager;
import me.wiefferink.areashop.nms.NMS;
import me.wiefferink.areashop.regions.ImportJobFactory;
import me.wiefferink.areashop.regions.RegionModule;
import me.wiefferink.areashop.tools.AdventureMessageBridge;
import me.wiefferink.areashop.tools.ForwardingMessageBridge;
import me.wiefferink.areashop.tools.LegacyMessageBridge;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import javax.annotation.Nonnull;
import java.io.File;
import java.util.Arrays;
public class AreaShopModule extends AbstractModule {
private final AreaShop instance;
private final WorldGuardInterface worldGuardInterface;
private final WorldEditInterface worldEditInterface;
private final NMS nms;
private final SignErrorLogger signErrorLogger;
private final AbstractModule[] extras;
public AreaShopModule(@Nonnull AreaShop instance,
@Nonnull NMS nms,
@Nonnull WorldEditInterface worldEditInterface,
@Nonnull WorldGuardInterface worldGuardInterface,
@Nonnull SignErrorLogger signErrorLogger,
@Nonnull AbstractModule... extras
) {
this.instance = instance;
this.nms = nms;
this.signErrorLogger = signErrorLogger;
this.worldEditInterface = worldEditInterface;
this.worldGuardInterface = worldGuardInterface;
this.extras = extras;
}
@Override
protected void configure() {
Arrays.stream(this.extras).forEach(this::install);
bind(Plugin.class).toInstance(this.instance);
bind(AreaShop.class).toInstance(this.instance);
bind(MessageBridge.class).to(ForwardingMessageBridge.class).asEagerSingleton();
bind(WorldGuardInterface.class).toInstance(this.worldGuardInterface);
bind(WorldEditInterface.class).toInstance(this.worldEditInterface);
bind(SignErrorLogger.class).toInstance(this.signErrorLogger);
// Setup managers
bind(IFileManager.class).to(FileManager.class).asEagerSingleton();
bind(FeatureManager.class).in(Singleton.class);
bind(CommandManager.class).in(Singleton.class);
bind(SignLinkerManager.class).in(Singleton.class);
install(new SignsModule());
install(new RegionModule());
install(new FactoryModuleBuilder().build(FeatureFactory.class));
install(new FactoryModuleBuilder().build(ImportJobFactory.class));
requestStaticInjection(Utils.class);
}
}
@@ -0,0 +1,34 @@
package me.wiefferink.areashop.modules;
import com.google.inject.AbstractModule;
import org.bukkit.Server;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.inventory.ItemFactory;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicesManager;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scoreboard.ScoreboardManager;
import javax.annotation.Nonnull;
public class BukkitModule extends AbstractModule {
private final Server server;
public BukkitModule(@Nonnull Server server) {
this.server = server;
}
@Override
protected void configure() {
bind(Server.class).toInstance(this.server);
bind(PluginManager.class).toInstance(this.server.getPluginManager());
bind(BukkitScheduler.class).toInstance(this.server.getScheduler());
bind(PluginManager.class).toInstance(this.server.getPluginManager());
bind(ServicesManager.class).toInstance(this.server.getServicesManager());
bind(ItemFactory.class).toInstance(this.server.getItemFactory());
bind(ConsoleCommandSender.class).toInstance(this.server.getConsoleSender());
bind(ScoreboardManager.class).toInstance(this.server.getScoreboardManager());
}
}
@@ -0,0 +1,37 @@
package me.wiefferink.areashop.modules;
import com.google.inject.AbstractModule;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.permission.Permission;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class DependencyModule extends AbstractModule {
private final WorldEditPlugin worldEditPlugin;
private final WorldGuardPlugin worldGuardPlugin;
private final Economy economy;
private final Permission permission;
public DependencyModule(@Nonnull WorldEditPlugin worldEditPlugin,
@Nonnull WorldGuardPlugin worldGuardPlugin,
@Nullable Economy economy,
@Nullable Permission permission
) {
this.worldEditPlugin = worldEditPlugin;
this.worldGuardPlugin = worldGuardPlugin;
this.economy = economy;
this.permission = permission;
}
@Override
protected void configure() {
bind(WorldEditPlugin.class).toInstance(this.worldEditPlugin);
bind(WorldGuardPlugin.class).toInstance(this.worldGuardPlugin);
bind(Economy.class).toInstance(this.economy);
bind(Permission.class).toInstance(this.permission);
}
}
@@ -0,0 +1,29 @@
package me.wiefferink.areashop.modules;
import com.google.inject.AbstractModule;
import me.wiefferink.areashop.adapters.platform.MinecraftPlatform;
import me.wiefferink.areashop.adapters.platform.OfflinePlayerHelper;
import me.wiefferink.areashop.nms.BlockBehaviourHelper;
import me.wiefferink.areashop.nms.NMS;
import me.wiefferink.areashop.tools.BukkitSchedulerExecutor;
import org.jetbrains.annotations.NotNull;
public class PlatformModule extends AbstractModule {
private final MinecraftPlatform minecraftPlatform;
private final NMS nms;
public PlatformModule(@NotNull MinecraftPlatform platform,
@NotNull NMS nms) {
this.minecraftPlatform = platform;
this.nms = nms;
}
@Override
protected void configure() {
bind(MinecraftPlatform.class).toInstance(this.minecraftPlatform);
bind(OfflinePlayerHelper.class).toInstance(this.minecraftPlatform.offlinePlayerHelper());
bind(NMS.class).toInstance(this.nms);
bind(BlockBehaviourHelper.class).toInstance(this.nms.blockBehaviourHelper());
bind(BukkitSchedulerExecutor.class).asEagerSingleton();
}
}
Loaded 100 of 242 files, more files were not shown because too many files have changed in this diff. Show more