├── .github
└── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── common
├── pom.xml
└── src
│ └── main
│ └── java
│ └── me
│ └── blvckbytes
│ └── quick_shop_search
│ ├── compatibility
│ ├── QuickShopEventConsumer.java
│ └── RemoteInteractionApi.java
│ ├── config
│ ├── MainSection.java
│ ├── PlayerMessagesSection.java
│ ├── PredicatesSection.java
│ ├── access_lists
│ │ ├── ShopAccessListSection.java
│ │ └── ShopAccessListsSection.java
│ ├── commands
│ │ ├── CommandsSection.java
│ │ ├── QuickShopSearchCommandSection.java
│ │ ├── QuickShopSearchLanguageCommandSection.java
│ │ ├── QuickShopSearchReloadCommandSection.java
│ │ └── SearchFlagSection.java
│ ├── cooldowns
│ │ ├── CooldownType.java
│ │ ├── CooldownsSection.java
│ │ ├── ShopTeleportCooldowns.java
│ │ └── TeleportToShopSection.java
│ ├── display_common
│ │ ├── GuiItemStackSection.java
│ │ ├── GuiSection.java
│ │ └── PaginatedGuiSection.java
│ ├── fees
│ │ ├── FeesDistanceRangeSection.java
│ │ ├── FeesDistanceRangesSection.java
│ │ ├── FeesOtherWorldSection.java
│ │ ├── FeesSection.java
│ │ └── FeesValuesSection.java
│ ├── integration
│ │ ├── EssentialsWarpsIntegrationSection.java
│ │ ├── PlayerWarpsIntegrationSection.java
│ │ └── WorldGuardIntegrationSection.java
│ ├── result_display
│ │ ├── ResultDisplayItemsSection.java
│ │ └── ResultDisplaySection.java
│ ├── slow_teleport
│ │ ├── SlotsMessagesSection.java
│ │ ├── SlowTeleportNotification.java
│ │ ├── SlowTeleportParametersSection.java
│ │ └── SlowTeleportSection.java
│ └── teleport_display
│ │ ├── TeleportDisplayItemsSection.java
│ │ └── TeleportDisplaySection.java
│ └── integration
│ ├── ChunkBucketedCache.java
│ ├── essentials_warps
│ ├── EssentialsWarpData.java
│ └── IEssentialsWarpsIntegration.java
│ ├── player_warps
│ ├── IPlayerWarpsIntegration.java
│ └── PlayerWarpData.java
│ └── worldguard
│ └── IWorldGuardIntegration.java
├── compatibility
├── qs_gt_6207_lte_6208
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── me
│ │ └── blvckbytes
│ │ └── quick_shop_search
│ │ └── compatibility
│ │ ├── QuickShopListener_GT_6207_LTE_6208.java
│ │ └── RemoteInteractionApi_GT_6207_LTE_6208.java
├── qs_gt_6208
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── me
│ │ └── blvckbytes
│ │ └── quick_shop_search
│ │ └── compatibility
│ │ ├── QuickShopListener_GT_6208.java
│ │ └── RemoteInteractionApi_GT_6208.java
└── qs_lte_6207
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── me
│ └── blvckbytes
│ └── quick_shop_search
│ └── compatibility
│ ├── QuickShopListener_LTE_6207.java
│ └── RemoteInteractionApi_LTE_6207.java
├── core
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── me
│ │ └── blvckbytes
│ │ └── quick_shop_search
│ │ ├── ChatPromptInstance.java
│ │ ├── ChatPromptManager.java
│ │ ├── OfflinePlayerRegistry.java
│ │ ├── PluginPermission.java
│ │ ├── QuickShopSearchPlugin.java
│ │ ├── SlowTeleportManager.java
│ │ ├── UidScopedNamedStampStore.java
│ │ ├── cache
│ │ ├── CachedShop.java
│ │ ├── CachedShopRegistry.java
│ │ ├── QuickShopVersionDependentFactory.java
│ │ ├── ShopScalarDiff.java
│ │ ├── ShopUpdate.java
│ │ └── ToggleResult.java
│ │ ├── command
│ │ ├── CommandSendListener.java
│ │ ├── QuickShopSearchCommand.java
│ │ ├── ReloadCommand.java
│ │ ├── SearchFlag.java
│ │ ├── SearchFlagsContainer.java
│ │ └── SubCommand_Advertise.java
│ │ ├── config
│ │ └── Base64TexturesResolverFunction.java
│ │ ├── display
│ │ ├── Display.java
│ │ ├── DisplayHandler.java
│ │ ├── result
│ │ │ ├── AsyncTaskQueue.java
│ │ │ ├── CalculatedFees.java
│ │ │ ├── DynamicPropertyProvider.java
│ │ │ ├── FilteringFunction.java
│ │ │ ├── LimitingFactor.java
│ │ │ ├── MaxUnitsResult.java
│ │ │ ├── PredicateSelection.java
│ │ │ ├── ResultDisplay.java
│ │ │ ├── ResultDisplayData.java
│ │ │ ├── ResultDisplayHandler.java
│ │ │ ├── SelectionState.java
│ │ │ ├── SelectionStateStore.java
│ │ │ ├── ShopFilteringCriteria.java
│ │ │ ├── ShopSortingCriteria.java
│ │ │ ├── SortingFunction.java
│ │ │ ├── SortingSelection.java
│ │ │ └── TeleportCooldownType.java
│ │ └── teleport
│ │ │ ├── TeleportDisplay.java
│ │ │ ├── TeleportDisplayData.java
│ │ │ └── TeleportDisplayHandler.java
│ │ └── integration
│ │ └── IntegrationRegistry.java
│ └── resources
│ ├── config
│ ├── config.yml
│ ├── de_de.txt
│ ├── en_us.txt
│ ├── tr_tr.txt
│ └── zh_cn.txt
│ └── plugin.yml
├── integration
├── essentials_warps_integration
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── me
│ │ └── blvckbytes
│ │ └── quick_shop_search
│ │ └── integration
│ │ └── essentials_warps
│ │ └── EssentialsWarpsIntegration.java
├── olziedev_player_warps_integration
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── me
│ │ └── blvckbytes
│ │ └── quick_shop_search
│ │ └── integration
│ │ └── player_warps
│ │ └── OlzieDevPlayerWarpsIntegration.java
├── revivalo_player_warps_integration
│ ├── libs
│ │ └── PlayerWarps-3.3.2.jar
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── me
│ │ └── blvckbytes
│ │ └── quick_shop_search
│ │ └── integration
│ │ └── player_warps
│ │ └── RevivaloPlayerWarpsIntegration.java
└── worldguard_integration
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── me
│ └── blvckbytes
│ └── quick_shop_search
│ └── integration
│ └── worldguard
│ └── WorldGuardIntegration.java
└── pom.xml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: blvckbytes
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea
8 | *.iws
9 | *.iml
10 | *.ipr
11 |
12 | ### Eclipse ###
13 | .apt_generated
14 | .classpath
15 | .factorypath
16 | .project
17 | .settings
18 | .springBeans
19 | .sts4-cache
20 |
21 | ### NetBeans ###
22 | /nbproject/private/
23 | /nbbuild/
24 | /dist/
25 | /nbdist/
26 | /.nb-gradle/
27 | build/
28 | !**/src/main/**/build/
29 | !**/src/test/**/build/
30 |
31 | ### VS Code ###
32 | .vscode/
33 |
34 | ### Mac OS ###
35 | .DS_Store
36 |
37 | dependency-reduced-pom.xml
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QuickShopSearch
2 |
3 | Quickly query all global shops, as managed by [QuickShop-Hikari](https://github.com/QuickShop-Community/QuickShop-Hikari),
4 | using predicates; filter, sort and browse through results. Find installation- and usage-documentation over at the
5 | corresponding [Docs-QuickShopSearch](https://blvckbytes.github.io/docs-quick-shop-search/) website.
--------------------------------------------------------------------------------
/common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | me.blvckbytes
9 | quickshopsearch-root
10 | ${revision}
11 |
12 |
13 | common
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 |
23 | jitpack.io
24 | https://jitpack.io
25 |
26 |
27 |
28 |
29 |
30 | com.ghostchu
31 | quickshop-api
32 | 6.2.0.6
33 | provided
34 |
35 |
36 | org.jetbrains
37 | annotations
38 | 24.1.0
39 |
40 |
41 | com.github.technicallycoded
42 | FoliaLib
43 | 0.4.3
44 | compile
45 |
46 |
47 | me.blvckbytes
48 | BukkitEvaluable
49 | 0.1
50 |
51 |
52 | me.blvckbytes
53 | ItemPredicateParser
54 | 0.0.18
55 |
56 |
57 | me.blvckbytes
58 | SyllablesMatcher
59 | 0.1
60 |
61 |
62 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/compatibility/QuickShopEventConsumer.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.compatibility;
2 |
3 | import com.ghostchu.quickshop.api.shop.Shop;
4 | import org.bukkit.inventory.ItemStack;
5 |
6 | import java.util.UUID;
7 |
8 | public interface QuickShopEventConsumer {
9 |
10 | void onPurchaseSuccess(Shop shop, int amount, UUID purchaserId);
11 |
12 | void onShopCreate(Shop shop);
13 |
14 | void onShopDelete(Shop shop);
15 |
16 | void onShopItemChange(Shop shop, ItemStack newItem);
17 |
18 | void onShopOwnerChange(Shop shop);
19 |
20 | void onShopSignUpdate(Shop shop);
21 |
22 | void onShopInventoryCalculate(Shop shop, int stock, int space);
23 |
24 | void onShopNameChange(Shop shop);
25 |
26 | void onShopPriceChange(Shop shop);
27 |
28 | void onShopTypeChange(Shop shop);
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/compatibility/RemoteInteractionApi.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.compatibility;
2 |
3 | import com.ghostchu.quickshop.api.shop.Shop;
4 | import org.bukkit.entity.Player;
5 |
6 | public interface RemoteInteractionApi {
7 |
8 | /**
9 | * Interact with a shop remotely, dispatching the same action as is completed when
10 | * trading manually, at the shop's physical location. Whether items are bought or
11 | * sold is automatically determined by the Shop's type and thus not a user-choice.
12 | */
13 | void interact(Player player, Shop shop, int amount);
14 |
15 | /**
16 | * Get the balance of a player within the shop's world, using the shop's currency
17 | */
18 | double getPlayerBalance(Player player, Shop shop);
19 |
20 | /**
21 | * Get the balance of the shop-owning player, within the shop's world, using the shop's currency
22 | */
23 | double getOwnerBalance(Shop shop);
24 |
25 | boolean withdrawAmount(Player player, Shop shop, double amount);
26 |
27 | boolean depositAmount(Player player, Shop shop, double amount);
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/MainSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 | import me.blvckbytes.quick_shop_search.config.access_lists.ShopAccessListsSection;
7 | import me.blvckbytes.quick_shop_search.config.commands.CommandsSection;
8 | import me.blvckbytes.quick_shop_search.config.cooldowns.CooldownsSection;
9 | import me.blvckbytes.quick_shop_search.config.fees.FeesSection;
10 | import me.blvckbytes.quick_shop_search.config.integration.EssentialsWarpsIntegrationSection;
11 | import me.blvckbytes.quick_shop_search.config.integration.PlayerWarpsIntegrationSection;
12 | import me.blvckbytes.quick_shop_search.config.integration.WorldGuardIntegrationSection;
13 | import me.blvckbytes.quick_shop_search.config.result_display.ResultDisplaySection;
14 | import me.blvckbytes.quick_shop_search.config.slow_teleport.SlowTeleportSection;
15 | import me.blvckbytes.quick_shop_search.config.teleport_display.TeleportDisplaySection;
16 |
17 | @CSAlways
18 | public class MainSection extends AConfigSection {
19 |
20 | public ResultDisplaySection resultDisplay;
21 | public TeleportDisplaySection teleportDisplay;
22 | public PlayerMessagesSection playerMessages;
23 | public PredicatesSection predicates;
24 | public CommandsSection commands;
25 | public ShopAccessListsSection shopAccessLists;
26 | public CooldownsSection cooldowns;
27 | public SlowTeleportSection slowTeleport;
28 | public PlayerWarpsIntegrationSection playerWarpsIntegration;
29 | public EssentialsWarpsIntegrationSection essentialsWarpsIntegration;
30 | public WorldGuardIntegrationSection worldGuardIntegration;
31 | public FeesSection fees;
32 |
33 | public MainSection(EvaluationEnvironmentBuilder baseEnvironment) {
34 | super(baseEnvironment);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/PlayerMessagesSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 |
7 | import javax.annotation.Nullable;
8 |
9 | public class PlayerMessagesSection extends AConfigSection {
10 |
11 | public @Nullable BukkitEvaluable emptyPredicate;
12 | public @Nullable BukkitEvaluable noMatches;
13 | public @Nullable BukkitEvaluable beforeQuerying;
14 | public @Nullable BukkitEvaluable beforeTeleporting;
15 | public @Nullable BukkitEvaluable beforeTeleportingNearestPlayerWarp;
16 | public @Nullable BukkitEvaluable beforeTeleportingNearestEssentialsWarp;
17 | public @Nullable BukkitEvaluable queryingAllShops;
18 | public @Nullable BukkitEvaluable usageQsslCommandLanguage;
19 | public @Nullable BukkitEvaluable unknownLanguageActionBar;
20 | public @Nullable BukkitEvaluable pluginReloadedSuccess;
21 | public @Nullable BukkitEvaluable pluginReloadedError;
22 | public @Nullable BukkitEvaluable predicateParseError;
23 | public @Nullable BukkitEvaluable searchFlagParseError;
24 | public @Nullable BukkitEvaluable shopInteractPromptBuying;
25 | public @Nullable BukkitEvaluable shopInteractPromptSelling;
26 | public @Nullable BukkitEvaluable shopInteractPromptTimeout;
27 | public @Nullable BukkitEvaluable shopInteractPromptDispatch;
28 | public @Nullable BukkitEvaluable shopInteractPromptInvalidInput;
29 | public @Nullable BukkitEvaluable shopInteractPromptCancelPrevious;
30 | public @Nullable BukkitEvaluable shopInteractPromptCancelCurrent;
31 | public @Nullable BukkitEvaluable shopInteractPromptLimitingFactorPlayerSpace;
32 | public @Nullable BukkitEvaluable shopInteractPromptLimitingFactorPlayerStock;
33 | public @Nullable BukkitEvaluable shopInteractPromptLimitingFactorShopSpace;
34 | public @Nullable BukkitEvaluable shopInteractPromptLimitingFactorShopStock;
35 | public @Nullable BukkitEvaluable shopInteractPromptLimitingFactorSellerFunds;
36 | public @Nullable BukkitEvaluable shopInteractPromptLimitingFactorBuyerFunds;
37 | public @Nullable BukkitEvaluable shopInteractPromptFeesWarning;
38 | public @Nullable BukkitEvaluable shopInteractPlayerHasNoSpace;
39 | public @Nullable BukkitEvaluable shopInteractPlayerHasNoStock;
40 | public @Nullable BukkitEvaluable shopInteractShopHasNoSpace;
41 | public @Nullable BukkitEvaluable shopInteractShopHasNoStock;
42 | public @Nullable BukkitEvaluable shopInteractSellerInsufficientFunds;
43 | public @Nullable BukkitEvaluable shopInteractBuyerInsufficientFunds;
44 | public @Nullable BukkitEvaluable shopInteractPendingFeesTask;
45 | public @Nullable BukkitEvaluable shopInteractCouldNotWithdrawFees;
46 | public @Nullable BukkitEvaluable shopInteractWithdrawnFees;
47 | public @Nullable BukkitEvaluable shopInteractPayedBackFees;
48 | public @Nullable BukkitEvaluable shopInteractCouldNotPayBackFees;
49 |
50 | public @Nullable BukkitEvaluable missingPermissionMainCommand;
51 | public @Nullable BukkitEvaluable missingPermissionLanguageCommand;
52 | public @Nullable BukkitEvaluable missingPermissionReloadCommand;
53 | public @Nullable BukkitEvaluable missingPermissionAdvertiseCommand;
54 | public @Nullable BukkitEvaluable missingPermissionFeatureSort;
55 | public @Nullable BukkitEvaluable missingPermissionFeatureFilter;
56 | public @Nullable BukkitEvaluable missingPermissionFeatureTeleport;
57 | public @Nullable BukkitEvaluable missingPermissionFeatureTeleportOtherWorld;
58 | public @Nullable BukkitEvaluable missingPermissionFeatureInteract;
59 | public @Nullable BukkitEvaluable missingPermissionFeatureInteractOtherWorld;
60 |
61 | public @Nullable BukkitEvaluable commandAdvertiseDescription;
62 | public @Nullable BukkitEvaluable commandAdvertiseNotLookingAtShop;
63 | public @Nullable BukkitEvaluable commandAdvertiseNotTheOwner;
64 | public @Nullable BukkitEvaluable commandAdvertiseToggleError;
65 | public @Nullable BukkitEvaluable commandAdvertiseEnabledSelf;
66 | public @Nullable BukkitEvaluable commandAdvertiseEnabledOther;
67 | public @Nullable BukkitEvaluable commandAdvertiseDisabledSelf;
68 | public @Nullable BukkitEvaluable commandAdvertiseDisabledOther;
69 | public @Nullable BukkitEvaluable commandAdvertiseDisallowedSelf;
70 | public @Nullable BukkitEvaluable commandAdvertiseDisallowedOther;
71 |
72 | public @Nullable BukkitEvaluable pendingCooldownFeatureTeleportSameShop;
73 | public @Nullable BukkitEvaluable pendingCooldownFeatureTeleportAnyShop;
74 | public @Nullable BukkitEvaluable pendingCooldownFeatureTeleportOtherWorldSameShop;
75 | public @Nullable BukkitEvaluable pendingCooldownFeatureTeleportOtherWorldAnyShop;
76 |
77 | public @Nullable BukkitEvaluable slowTeleportHasMoved;
78 | public @Nullable BukkitEvaluable slowTeleportHasBeenAttacked;
79 | public @Nullable BukkitEvaluable slowTeleportAttackedSomebody;
80 | public @Nullable BukkitEvaluable slowTeleportTookDamageByNonPlayer;
81 |
82 | public @Nullable BukkitEvaluable nearestPlayerWarpBanned;
83 |
84 | public PlayerMessagesSection(EvaluationEnvironmentBuilder baseEnvironment) {
85 | super(baseEnvironment);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/PredicatesSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 | import me.blvckbytes.item_predicate_parser.translation.TranslationLanguage;
6 |
7 | public class PredicatesSection extends AConfigSection {
8 |
9 | public TranslationLanguage mainLanguage;
10 |
11 | public PredicatesSection(EvaluationEnvironmentBuilder baseEnvironment) {
12 | super(baseEnvironment);
13 |
14 | this.mainLanguage = TranslationLanguage.ENGLISH_US;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/access_lists/ShopAccessListSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.access_lists;
2 |
3 | import me.blvckbytes.bbconfigmapper.MappingError;
4 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
5 | import me.blvckbytes.bbconfigmapper.sections.CSIgnore;
6 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
7 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
8 | import org.bukkit.Material;
9 |
10 | import java.lang.reflect.Field;
11 | import java.util.ArrayList;
12 | import java.util.HashSet;
13 | import java.util.List;
14 | import java.util.Set;
15 |
16 | public class ShopAccessListSection extends AConfigSection {
17 |
18 | public boolean isAllowTypes;
19 | public List types;
20 |
21 | @CSIgnore
22 | public final Set typeMaterials;
23 |
24 | public ShopAccessListSection(EvaluationEnvironmentBuilder baseEnvironment) {
25 | super(baseEnvironment);
26 |
27 | // It's very important that this property defaults to false, as an empty types-list
28 | // thereby results in all shops passing, which is desired behavior.
29 | this.isAllowTypes = false;
30 | this.types = new ArrayList<>();
31 | this.typeMaterials = new HashSet<>();
32 | }
33 |
34 | @Override
35 | public void afterParsing(List fields) throws Exception {
36 | super.afterParsing(fields);
37 |
38 | for (var type : types) {
39 | var interpretedMaterial = type.asXMaterial(builtBaseEnvironment);
40 |
41 | if (interpretedMaterial == null)
42 | throw new MappingError("Could not interpret access-list type \"" + type.value + "\" as a material");
43 |
44 | typeMaterials.add(interpretedMaterial.parseMaterial());
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/access_lists/ShopAccessListsSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.access_lists;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSNamed;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | public class ShopAccessListsSection extends AConfigSection {
12 |
13 | @CSNamed(name="default")
14 | public @Nullable ShopAccessListSection _default;
15 |
16 | public Map permissions;
17 |
18 | public ShopAccessListsSection(EvaluationEnvironmentBuilder baseEnvironment) {
19 | super(baseEnvironment);
20 |
21 | this.permissions = new HashMap<>();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/commands/CommandsSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.commands;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 |
7 | @CSAlways
8 | public class CommandsSection extends AConfigSection {
9 |
10 | public QuickShopSearchCommandSection quickShopSearch;
11 | public QuickShopSearchLanguageCommandSection quickShopSearchLanguage;
12 | public QuickShopSearchReloadCommandSection quickShopSearchReload;
13 |
14 | public CommandsSection(EvaluationEnvironmentBuilder baseEnvironment) {
15 | super(baseEnvironment);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/commands/QuickShopSearchCommandSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.commands;
2 |
3 | import me.blvckbytes.bukkitevaluable.section.ACommandSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | public class QuickShopSearchCommandSection extends ACommandSection {
10 |
11 | public static final String INITIAL_NAME = "quickshopsearch";
12 |
13 | public Map searchFlags;
14 |
15 | public QuickShopSearchCommandSection(EvaluationEnvironmentBuilder baseEnvironment) {
16 | super(INITIAL_NAME, baseEnvironment);
17 |
18 | this.searchFlags = new HashMap<>();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/commands/QuickShopSearchLanguageCommandSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.commands;
2 |
3 | import me.blvckbytes.bukkitevaluable.section.ACommandSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 |
6 | public class QuickShopSearchLanguageCommandSection extends ACommandSection {
7 |
8 | public static final String INITIAL_NAME = "quickshopsearchlanguage";
9 |
10 | public QuickShopSearchLanguageCommandSection(EvaluationEnvironmentBuilder baseEnvironment) {
11 | super(INITIAL_NAME, baseEnvironment);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/commands/QuickShopSearchReloadCommandSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.commands;
2 |
3 | import me.blvckbytes.bukkitevaluable.section.ACommandSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 |
6 | public class QuickShopSearchReloadCommandSection extends ACommandSection {
7 |
8 | public static final String INITIAL_NAME = "quickshopsearchreload";
9 |
10 | public QuickShopSearchReloadCommandSection(EvaluationEnvironmentBuilder baseEnvironment) {
11 | super(INITIAL_NAME, baseEnvironment);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/commands/SearchFlagSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.commands;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 |
6 | import javax.annotation.Nullable;
7 | import java.util.List;
8 |
9 | public class SearchFlagSection extends AConfigSection {
10 |
11 | public @Nullable String name;
12 | public @Nullable List suggestions;
13 |
14 | public SearchFlagSection(EvaluationEnvironmentBuilder baseEnvironment) {
15 | super(baseEnvironment);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/cooldowns/CooldownType.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.cooldowns;
2 |
3 | public enum CooldownType {
4 | SAME_SHOP,
5 | ANY_SHOP,
6 | OTHER_WORLD_SAME_SHOP,
7 | OTHER_WORLD_ANY_SHOP
8 | }
9 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/cooldowns/CooldownsSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.cooldowns;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
6 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
7 |
8 | @CSAlways
9 | public class CooldownsSection extends AConfigSection {
10 |
11 | public BukkitEvaluable cooldownFormat;
12 | public TeleportToShopSection teleportToShop;
13 |
14 | public CooldownsSection(EvaluationEnvironmentBuilder baseEnvironment) {
15 | super(baseEnvironment);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/cooldowns/ShopTeleportCooldowns.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.cooldowns;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 |
6 | public class ShopTeleportCooldowns extends AConfigSection {
7 |
8 | public long sameShop;
9 | public long anyShop;
10 | public long otherWorldSameShop;
11 | public long otherWorldAnyShop;
12 |
13 | public ShopTeleportCooldowns(EvaluationEnvironmentBuilder baseEnvironment) {
14 | super(baseEnvironment);
15 | }
16 |
17 | public long getCooldownMillis(CooldownType type) {
18 | return switch (type) {
19 | case SAME_SHOP -> sameShop;
20 | case ANY_SHOP -> anyShop;
21 | case OTHER_WORLD_SAME_SHOP -> otherWorldSameShop;
22 | case OTHER_WORLD_ANY_SHOP -> otherWorldAnyShop;
23 | } * 1000;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/cooldowns/TeleportToShopSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.cooldowns;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.bbconfigmapper.sections.CSNamed;
6 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
7 |
8 | import java.util.LinkedHashMap;
9 | import java.util.Map;
10 |
11 | @CSAlways
12 | public class TeleportToShopSection extends AConfigSection {
13 |
14 | @CSNamed(name="default")
15 | public ShopTeleportCooldowns _default;
16 |
17 | public Map groups;
18 |
19 | public TeleportToShopSection(EvaluationEnvironmentBuilder baseEnvironment) {
20 | super(baseEnvironment);
21 |
22 | this.groups = new LinkedHashMap<>();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/display_common/GuiItemStackSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.display_common;
2 |
3 | import me.blvckbytes.bbconfigmapper.ScalarType;
4 | import me.blvckbytes.bbconfigmapper.sections.CSIgnore;
5 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
6 | import me.blvckbytes.bukkitevaluable.IItemBuildable;
7 | import me.blvckbytes.bukkitevaluable.section.ItemStackSection;
8 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
9 | import me.blvckbytes.gpeee.interpreter.IEvaluationEnvironment;
10 | import org.bukkit.inventory.Inventory;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | import java.lang.reflect.Field;
14 | import java.util.Collections;
15 | import java.util.List;
16 | import java.util.Set;
17 |
18 | public class GuiItemStackSection extends ItemStackSection {
19 |
20 | private @Nullable BukkitEvaluable slots;
21 |
22 | @CSIgnore
23 | private IItemBuildable buildable;
24 |
25 | @CSIgnore
26 | private @Nullable Set displaySlots;
27 |
28 | public GuiItemStackSection(EvaluationEnvironmentBuilder baseEnvironment) {
29 | super(baseEnvironment);
30 | }
31 |
32 | @Override
33 | public void afterParsing(List fields) throws Exception {
34 | super.afterParsing(fields);
35 |
36 | this.buildable = asItem();
37 | }
38 |
39 | public IItemBuildable getBuildable() {
40 | return buildable;
41 | }
42 |
43 | public void initializeDisplaySlots(IEvaluationEnvironment inventoryEnvironment) {
44 | this.displaySlots = Collections.unmodifiableSet(
45 | slots == null
46 | ? Set.of()
47 | : slots.asSet(ScalarType.INT, inventoryEnvironment)
48 | );
49 | }
50 |
51 | public Set getDisplaySlots() {
52 | return displaySlots == null ? Set.of() : displaySlots;
53 | }
54 |
55 | public void renderInto(Inventory inventory, IEvaluationEnvironment environment) {
56 | if (displaySlots == null)
57 | return;
58 |
59 | var item = buildable.build(environment);
60 | var inventorySize = inventory.getSize();
61 |
62 | for (var slot : displaySlots) {
63 | if (slot < inventorySize)
64 | inventory.setItem(slot, item);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/display_common/GuiSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.display_common;
2 |
3 | import me.blvckbytes.bbconfigmapper.MappingError;
4 | import me.blvckbytes.bbconfigmapper.ScalarType;
5 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
6 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
7 | import me.blvckbytes.bbconfigmapper.sections.CSDecide;
8 | import me.blvckbytes.bbconfigmapper.sections.CSIgnore;
9 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
10 | import me.blvckbytes.gpeee.GPEEE;
11 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
12 | import me.blvckbytes.gpeee.interpreter.IEvaluationEnvironment;
13 | import org.bukkit.Bukkit;
14 | import org.bukkit.inventory.Inventory;
15 | import org.jetbrains.annotations.Nullable;
16 |
17 | import java.lang.reflect.Field;
18 | import java.util.List;
19 |
20 | public class GuiSection extends AConfigSection {
21 |
22 | private static final int DEFAULT_ROWS = 3;
23 |
24 | protected @Nullable BukkitEvaluable title;
25 | protected @Nullable BukkitEvaluable rows;
26 |
27 | @CSAlways
28 | @CSDecide
29 | public T items;
30 |
31 | @CSIgnore
32 | protected final Class itemsSectionClass;
33 |
34 | @CSIgnore
35 | protected int _rows, lastSlot;
36 |
37 | @CSIgnore
38 | public IEvaluationEnvironment inventoryEnvironment;
39 |
40 | public GuiSection(Class itemsSectionClass, EvaluationEnvironmentBuilder baseEnvironment) {
41 | super(baseEnvironment);
42 | this.itemsSectionClass = itemsSectionClass;
43 | }
44 |
45 | @Override
46 | public @Nullable Class> runtimeDecide(String field) {
47 | if (field.equals("items"))
48 | return itemsSectionClass;
49 |
50 | return super.runtimeDecide(field);
51 | }
52 |
53 | @Override
54 | public void afterParsing(List fields) throws Exception {
55 | super.afterParsing(fields);
56 |
57 | _rows = rows == null ? DEFAULT_ROWS : rows.asScalar(ScalarType.INT, GPEEE.EMPTY_ENVIRONMENT);
58 |
59 | if (_rows < 1 || _rows > 6)
60 | throw new MappingError("Rows out of range [1;6]");
61 |
62 | lastSlot = _rows * 9 - 1;
63 |
64 | inventoryEnvironment = new EvaluationEnvironmentBuilder()
65 | .withStaticVariable("number_of_rows", _rows)
66 | .withStaticVariable("last_slot", lastSlot)
67 | .build(builtBaseEnvironment);
68 |
69 | for (var field : itemsSectionClass.getDeclaredFields()) {
70 | if (!GuiItemStackSection.class.isAssignableFrom(field.getType()))
71 | continue;
72 |
73 | field.setAccessible(true);
74 | ((GuiItemStackSection) field.get(items)).initializeDisplaySlots(inventoryEnvironment);
75 | }
76 | }
77 |
78 | public Inventory createInventory(IEvaluationEnvironment environment) {
79 | if (title == null)
80 | return Bukkit.createInventory(null, _rows * 9);
81 |
82 | return Bukkit.createInventory(null, _rows * 9, title.asScalar(ScalarType.STRING, environment));
83 | }
84 |
85 | public int getRows() {
86 | return _rows;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/display_common/PaginatedGuiSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.display_common;
2 |
3 | import me.blvckbytes.bbconfigmapper.MappingError;
4 | import me.blvckbytes.bbconfigmapper.ScalarType;
5 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
6 | import me.blvckbytes.bbconfigmapper.sections.CSIgnore;
7 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
8 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import java.lang.reflect.Field;
12 | import java.util.List;
13 | import java.util.Set;
14 |
15 | public class PaginatedGuiSection extends GuiSection {
16 |
17 | protected @Nullable BukkitEvaluable paginationSlots;
18 |
19 | @CSIgnore
20 | private Set _paginationSlots;
21 |
22 | public PaginatedGuiSection(Class itemsSectionClass, EvaluationEnvironmentBuilder baseEnvironment) {
23 | super(itemsSectionClass, baseEnvironment);
24 | }
25 |
26 | @Override
27 | public void afterParsing(List fields) throws Exception {
28 | super.afterParsing(fields);
29 |
30 | _paginationSlots = paginationSlots == null ? Set.of() : paginationSlots.asSet(ScalarType.INT, inventoryEnvironment);
31 |
32 | for (var paginationSlot : _paginationSlots) {
33 | if (paginationSlot < 0 || paginationSlot > lastSlot)
34 | throw new MappingError("Pagination slot " + paginationSlot + " out of range [0;" + lastSlot + "]");
35 | }
36 |
37 | for (var field : itemsSectionClass.getDeclaredFields()) {
38 | if (!GuiItemStackSection.class.isAssignableFrom(field.getType()))
39 | continue;
40 |
41 | field.setAccessible(true);
42 |
43 | GuiItemStackSection itemSection = (GuiItemStackSection) field.get(items);
44 |
45 | if (itemSection == null)
46 | continue;
47 |
48 | for (var itemSlot : itemSection.getDisplaySlots()) {
49 | if (itemSlot < 0 || itemSlot > lastSlot)
50 | throw new MappingError("Slot " + itemSlot + " of item " + field.getName() + " out of range [0;" + lastSlot + "]");
51 |
52 | if (_paginationSlots.contains(itemSlot))
53 | throw new MappingError("Slot " + itemSlot + " of item " + field.getName() + " conflicts with pagination-slots " + paginationSlots);
54 | }
55 | }
56 | }
57 |
58 | public Set getPaginationSlots() {
59 | return _paginationSlots;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/fees/FeesDistanceRangeSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.fees;
2 |
3 | import me.blvckbytes.bbconfigmapper.MappingError;
4 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
5 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
6 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
7 |
8 | import java.lang.reflect.Field;
9 | import java.util.LinkedHashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | @CSAlways
14 | public class FeesDistanceRangeSection extends AConfigSection {
15 |
16 | public long minDistance;
17 | public long maxDistance;
18 |
19 | public FeesValuesSection permissionNamesFallback;
20 |
21 | public Map permissionNames;
22 |
23 | public FeesDistanceRangeSection(EvaluationEnvironmentBuilder baseEnvironment) {
24 | super(baseEnvironment);
25 |
26 | this.permissionNames = new LinkedHashMap<>();
27 | }
28 |
29 | @Override
30 | public void afterParsing(List fields) throws Exception {
31 | if (minDistance < 0)
32 | throw new MappingError("The value of \"minDistance\" cannot be less than zero");
33 |
34 | if (maxDistance < 0)
35 | throw new MappingError("The value of \"maxDistance\" cannot be less than zero");
36 |
37 | if (maxDistance < minDistance)
38 | throw new MappingError("The value of \"maxDistance\" cannot be less than \"minDistance\"");
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/fees/FeesDistanceRangesSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.fees;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | @CSAlways
11 | public class FeesDistanceRangesSection extends AConfigSection {
12 |
13 | public List distanceRanges;
14 |
15 | public FeesDistanceRangeSection distanceRangesFallback;
16 |
17 | public FeesDistanceRangesSection(EvaluationEnvironmentBuilder baseEnvironment) {
18 | super(baseEnvironment);
19 |
20 | this.distanceRanges = new ArrayList<>();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/fees/FeesOtherWorldSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.fees;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 |
7 | import java.util.LinkedHashMap;
8 | import java.util.Map;
9 |
10 | @CSAlways
11 | public class FeesOtherWorldSection extends AConfigSection {
12 |
13 | public FeesValuesSection worldsFallback;
14 |
15 | public Map worlds;
16 |
17 | public FeesOtherWorldSection(EvaluationEnvironmentBuilder baseEnvironment) {
18 | super(baseEnvironment);
19 |
20 | this.worlds = new LinkedHashMap<>();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/fees/FeesSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.fees;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 |
7 | import java.util.LinkedHashMap;
8 | import java.util.Map;
9 |
10 | @CSAlways
11 | public class FeesSection extends AConfigSection {
12 |
13 | public boolean enabled;
14 |
15 | public long feesPayBackTimeoutTicks;
16 |
17 | public FeesDistanceRangesSection worldsFallback;
18 |
19 | public FeesOtherWorldSection otherWorld;
20 |
21 | public Map worlds;
22 |
23 | public FeesSection(EvaluationEnvironmentBuilder baseEnvironment) {
24 | super(baseEnvironment);
25 |
26 | this.enabled = false;
27 | this.worlds = new LinkedHashMap<>();
28 | this.feesPayBackTimeoutTicks = 40;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/fees/FeesValuesSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.fees;
2 |
3 | import me.blvckbytes.bbconfigmapper.MappingError;
4 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 |
7 | import java.lang.reflect.Field;
8 | import java.util.List;
9 |
10 | public class FeesValuesSection extends AConfigSection {
11 |
12 | public double absoluteBuy;
13 | public double relativeBuy;
14 | public double absoluteSell;
15 | public double relativeSell;
16 |
17 | public int priority;
18 |
19 | public FeesValuesSection(EvaluationEnvironmentBuilder baseEnvironment) {
20 | super(baseEnvironment);
21 | }
22 |
23 | @Override
24 | public void afterParsing(List fields) throws Exception {
25 | super.afterParsing(fields);
26 |
27 | if (relativeBuy < 0 || relativeBuy > 100)
28 | throw new MappingError("Property \"relativeBuy\" is not within the range of 0 to 100 (percent)");
29 |
30 | if (relativeSell < 0 || relativeSell > 100)
31 | throw new MappingError("Property \"relativeSell\" is not within the range of 0 to 100 (percent)");
32 |
33 | if (absoluteBuy < 0)
34 | throw new MappingError("Property \"absoluteBuy\" cannot be less than zero");
35 |
36 | if (absoluteSell < 0)
37 | throw new MappingError("Property \"absoluteSell\" cannot be less than zero");
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/integration/EssentialsWarpsIntegrationSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.integration;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 |
6 | public class EssentialsWarpsIntegrationSection extends AConfigSection {
7 |
8 | public int nearestWarpBlockRadius;
9 | public boolean enabled;
10 | public boolean displayNearestInIcon;
11 | public boolean withinSameRegion;
12 |
13 | public EssentialsWarpsIntegrationSection(EvaluationEnvironmentBuilder baseEnvironment) {
14 | super(baseEnvironment);
15 |
16 | this.enabled = false;
17 | this.nearestWarpBlockRadius = 15;
18 | this.displayNearestInIcon = false;
19 | this.withinSameRegion = false;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/integration/PlayerWarpsIntegrationSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.integration;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 |
6 | public class PlayerWarpsIntegrationSection extends AConfigSection {
7 |
8 | public int nearestWarpBlockRadius;
9 | public boolean enabled;
10 | public boolean displayNearestInIcon;
11 | public int updatePeriodSeconds;
12 | public boolean withinSameRegion;
13 |
14 | public PlayerWarpsIntegrationSection(EvaluationEnvironmentBuilder baseEnvironment) {
15 | super(baseEnvironment);
16 |
17 | this.enabled = false;
18 | this.nearestWarpBlockRadius = 15;
19 | this.displayNearestInIcon = false;
20 | this.updatePeriodSeconds = 30;
21 | this.withinSameRegion = false;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/integration/WorldGuardIntegrationSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.integration;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 |
6 | import java.util.HashSet;
7 | import java.util.Set;
8 |
9 | public class WorldGuardIntegrationSection extends AConfigSection {
10 |
11 | public boolean enabled;
12 | public Set ignoredPriorities;
13 | public Set ignoredIds;
14 | public Set advertiseIdsAllowList;
15 | public boolean autoAdvertiseIfInAllowList;
16 | public boolean disableAdvertiseIfNotAllowed;
17 |
18 | public WorldGuardIntegrationSection(EvaluationEnvironmentBuilder baseEnvironment) {
19 | super(baseEnvironment);
20 |
21 | this.enabled = false;
22 | this.ignoredPriorities = new HashSet<>();
23 | this.ignoredIds = new HashSet<>();
24 | this.advertiseIdsAllowList = new HashSet<>();
25 | this.autoAdvertiseIfInAllowList = false;
26 | this.disableAdvertiseIfNotAllowed = false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/result_display/ResultDisplayItemsSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.result_display;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.bukkitevaluable.section.ItemStackSection;
6 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
7 | import me.blvckbytes.quick_shop_search.config.display_common.GuiItemStackSection;
8 |
9 | @CSAlways
10 | public class ResultDisplayItemsSection extends AConfigSection {
11 |
12 | public GuiItemStackSection previousPage;
13 | public GuiItemStackSection nextPage;
14 | public GuiItemStackSection sorting;
15 | public GuiItemStackSection filtering;
16 | public GuiItemStackSection activeSearch;
17 | public GuiItemStackSection filler;
18 | public ItemStackSection representativePatch;
19 |
20 | public ResultDisplayItemsSection(EvaluationEnvironmentBuilder baseEnvironment) {
21 | super(baseEnvironment);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/result_display/ResultDisplaySection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.result_display;
2 |
3 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
4 | import me.blvckbytes.quick_shop_search.config.display_common.PaginatedGuiSection;
5 |
6 | public class ResultDisplaySection extends PaginatedGuiSection {
7 |
8 | public String chatPromptAllSentinel;
9 | public String chatPromptCancelSentinel;
10 |
11 | public ResultDisplaySection(EvaluationEnvironmentBuilder baseEnvironment) {
12 | super(ResultDisplayItemsSection.class, baseEnvironment);
13 |
14 | this.chatPromptAllSentinel = "all";
15 | this.chatPromptCancelSentinel = "cancel";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/slow_teleport/SlotsMessagesSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.slow_teleport;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 |
7 | import javax.annotation.Nullable;
8 |
9 | public class SlotsMessagesSection extends AConfigSection {
10 |
11 | public @Nullable BukkitEvaluable messageTitle;
12 | public @Nullable BukkitEvaluable messageSubTitle;
13 | public @Nullable BukkitEvaluable messageActionBar;
14 | public @Nullable BukkitEvaluable messageChat;
15 |
16 | public SlotsMessagesSection(EvaluationEnvironmentBuilder baseEnvironment) {
17 | super(baseEnvironment);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/slow_teleport/SlowTeleportNotification.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.slow_teleport;
2 |
3 | import com.cryptomorin.xseries.XSound;
4 | import me.blvckbytes.bbconfigmapper.MappingError;
5 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
6 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
7 | import me.blvckbytes.bbconfigmapper.sections.CSIgnore;
8 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
9 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
10 |
11 | import javax.annotation.Nullable;
12 | import java.lang.reflect.Field;
13 | import java.util.List;
14 |
15 | public class SlowTeleportNotification extends AConfigSection {
16 |
17 | @CSAlways
18 | public SlotsMessagesSection messages;
19 |
20 | public @Nullable BukkitEvaluable sound;
21 |
22 | @CSIgnore
23 | public @Nullable XSound _sound;
24 |
25 | public float soundVolume;
26 | public float soundPitch;
27 |
28 | public SlowTeleportNotification(EvaluationEnvironmentBuilder baseEnvironment) {
29 | super(baseEnvironment);
30 |
31 | this.soundVolume = 1;
32 | this.soundPitch = 1;
33 | }
34 |
35 | @Override
36 | public void afterParsing(List fields) throws Exception {
37 | super.afterParsing(fields);
38 |
39 | if (sound != null) {
40 | if ((_sound = sound.asXSound(builtBaseEnvironment)) == null)
41 | throw new MappingError("Property \"sound\" could not be corresponded to any valid sound");
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/slow_teleport/SlowTeleportParametersSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.slow_teleport;
2 |
3 | import me.blvckbytes.bbconfigmapper.MappingError;
4 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
5 | import me.blvckbytes.bbconfigmapper.sections.CSIgnore;
6 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | import java.lang.reflect.Field;
10 | import java.util.HashMap;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | public class SlowTeleportParametersSection extends AConfigSection {
15 |
16 | public int durationSeconds;
17 |
18 | public @Nullable SlowTeleportNotification fallbackNotification;
19 |
20 | public Map notificationAtSeconds;
21 |
22 | @CSIgnore
23 | public Map _notificationAtSeconds;
24 |
25 | public SlowTeleportParametersSection(EvaluationEnvironmentBuilder baseEnvironment) {
26 | super(baseEnvironment);
27 |
28 | this.notificationAtSeconds = new HashMap<>();
29 | this._notificationAtSeconds = new HashMap<>();
30 | }
31 |
32 | @Override
33 | public void afterParsing(List fields) throws Exception {
34 | super.afterParsing(fields);
35 |
36 | if (durationSeconds < 0)
37 | throw new MappingError("Property \"durationSeconds\" is less than zero!");
38 |
39 | for (var entry : notificationAtSeconds.entrySet()) {
40 | String key = entry.getKey();
41 | int secondsValue;
42 |
43 | try {
44 | secondsValue = Integer.parseInt(key);
45 | } catch (NumberFormatException ignored) {
46 | throw new MappingError("Property \"notificationAtSeconds." + key + "\" is not a valid integer!");
47 | }
48 |
49 | if (secondsValue < 0)
50 | throw new MappingError("Property \"notificationAtSeconds." + key + "\" is less than zero!");
51 |
52 | if (_notificationAtSeconds.put(secondsValue, entry.getValue()) != null)
53 | throw new MappingError("Property \"notificationAtSeconds." + key + "\" is a duplicate!");
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/slow_teleport/SlowTeleportSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.slow_teleport;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 |
7 | @CSAlways
8 | public class SlowTeleportSection extends AConfigSection {
9 |
10 | public boolean cancelIfDamagedByPlayer;
11 | public boolean cancelIfDamagedByNonPlayer;
12 | public int combatLogCoolOffDurationSeconds;
13 |
14 | public SlowTeleportParametersSection whenInCombat;
15 | public SlowTeleportParametersSection whenNotInCombat;
16 |
17 | public SlowTeleportSection(EvaluationEnvironmentBuilder baseEnvironment) {
18 | super(baseEnvironment);
19 |
20 | this.cancelIfDamagedByPlayer = true;
21 | this.cancelIfDamagedByNonPlayer = false;
22 | this.combatLogCoolOffDurationSeconds = 10;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/teleport_display/TeleportDisplayItemsSection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.teleport_display;
2 |
3 | import me.blvckbytes.bbconfigmapper.sections.AConfigSection;
4 | import me.blvckbytes.bbconfigmapper.sections.CSAlways;
5 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
6 | import me.blvckbytes.quick_shop_search.config.display_common.GuiItemStackSection;
7 |
8 | @CSAlways
9 | public class TeleportDisplayItemsSection extends AConfigSection {
10 |
11 | public GuiItemStackSection back;
12 | public GuiItemStackSection shopLocation;
13 | public GuiItemStackSection nearestPlayerWarpLocation;
14 | public GuiItemStackSection nearestEssentialsWarpLocation;
15 | public GuiItemStackSection filler;
16 |
17 | public TeleportDisplayItemsSection(EvaluationEnvironmentBuilder baseEnvironment) {
18 | super(baseEnvironment);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/config/teleport_display/TeleportDisplaySection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config.teleport_display;
2 |
3 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
4 | import me.blvckbytes.quick_shop_search.config.display_common.GuiSection;
5 |
6 | public class TeleportDisplaySection extends GuiSection {
7 |
8 | public TeleportDisplaySection(EvaluationEnvironmentBuilder baseEnvironment) {
9 | super(TeleportDisplayItemsSection.class, baseEnvironment);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/integration/ChunkBucketedCache.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration;
2 |
3 | import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap;
4 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
5 | import org.bukkit.Location;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | import java.util.*;
9 | import java.util.concurrent.ConcurrentHashMap;
10 | import java.util.function.Predicate;
11 |
12 | public abstract class ChunkBucketedCache {
13 |
14 | private final Map>> itemsByChunkHashByWorldId;
15 |
16 | protected ChunkBucketedCache() {
17 | this.itemsByChunkHashByWorldId = new ConcurrentHashMap<>();
18 | }
19 |
20 | protected abstract @Nullable Location extractItemLocation(T item);
21 |
22 | protected abstract boolean doItemsEqual(T a, T b);
23 |
24 | protected void clear() {
25 | itemsByChunkHashByWorldId.clear();
26 | }
27 |
28 | protected void registerItem(T item) {
29 | var bukkitLocation = extractItemLocation(item);
30 |
31 | if (bukkitLocation == null)
32 | return;
33 |
34 | var itemWorld = bukkitLocation.getWorld();
35 |
36 | if (itemWorld == null)
37 | return;
38 |
39 | var itemBucket = itemsByChunkHashByWorldId
40 | .computeIfAbsent(itemWorld.getUID(), k -> new Long2ObjectAVLTreeMap<>())
41 | .computeIfAbsent(fastChunkHash(bukkitLocation), k -> new ArrayList<>());
42 |
43 | // Ensure "set-like" behaviour, based on whatever criteria the equality-checker implements
44 | for (var iterator = itemBucket.iterator(); iterator.hasNext();) {
45 | if (doItemsEqual(item, iterator.next())) {
46 | iterator.remove();
47 | return;
48 | }
49 | }
50 |
51 | itemBucket.add(item);
52 | }
53 |
54 | protected void unregisterItem(T item) {
55 | var bukkitLocation = extractItemLocation(item);
56 |
57 | if (bukkitLocation == null)
58 | return;
59 |
60 | var itemWorld = bukkitLocation.getWorld();
61 |
62 | if (itemWorld == null)
63 | return;
64 |
65 | var itemsByChunkHash = itemsByChunkHashByWorldId.get(itemWorld.getUID());
66 |
67 | if (itemsByChunkHash == null)
68 | return;
69 |
70 | var items = itemsByChunkHash.get(fastChunkHash(bukkitLocation));
71 |
72 | if (items == null)
73 | return;
74 |
75 | for (var iterator = items.iterator(); iterator.hasNext();) {
76 | if (doItemsEqual(item, iterator.next())) {
77 | iterator.remove();
78 | return;
79 | }
80 | }
81 | }
82 |
83 | private class ClosestItemSession {
84 | Location origin;
85 | int originChunkX;
86 | int originChunkY;
87 | int originChunkZ;
88 | Long2ObjectMap> buckets;
89 | @Nullable Predicate matchPredicate;
90 |
91 | T closestItem = null;
92 | int closestBlockDistanceSquared = 0;
93 | int closestChunkDistanceSquared = 0;
94 |
95 | ClosestItemSession(Location origin, Long2ObjectMap> buckets, @Nullable Predicate matchPredicate) {
96 | this.origin = origin;
97 | this.buckets = buckets;
98 | this.matchPredicate = matchPredicate;
99 |
100 | this.originChunkX = origin.getBlockX() >> 4;
101 | this.originChunkY = origin.getBlockY() >> 4;
102 | this.originChunkZ = origin.getBlockZ() >> 4;
103 | }
104 |
105 | void visitItems(int deltaChunkX, int deltaChunkY, int deltaChunkZ) {
106 | var chunkHash = fastChunkHash(originChunkX + deltaChunkX, originChunkY + deltaChunkY, originChunkZ + deltaChunkZ);
107 | var bucket = buckets.get(chunkHash);
108 |
109 | if (bucket == null)
110 | return;
111 |
112 | for (var item : bucket) {
113 | var itemLocation = extractItemLocation(item);
114 | var distanceSquared = computeDistanceSquared(itemLocation, origin);
115 |
116 | if (closestItem == null || closestBlockDistanceSquared > distanceSquared) {
117 | if (matchPredicate != null && !matchPredicate.test(itemLocation))
118 | continue;
119 |
120 | closestBlockDistanceSquared = distanceSquared;
121 | closestChunkDistanceSquared = distanceSquared >> 8;
122 | closestItem = item;
123 | }
124 | }
125 | }
126 | }
127 |
128 | protected @Nullable T findClosestItem(Location origin, int blockRadius, @Nullable Predicate matchPredicate) {
129 | var world = origin.getWorld();
130 |
131 | if (world == null)
132 | return null;
133 |
134 | var itemsByChunkHash = itemsByChunkHashByWorldId.get(world.getUID());
135 |
136 | if (itemsByChunkHash == null)
137 | return null;
138 |
139 | var closestSession = new ClosestItemSession(origin, itemsByChunkHash, matchPredicate);
140 |
141 | var radiusInChunks = (blockRadius + 15) >> 4;
142 | var maxDistanceSquared = square(radiusInChunks) * 2;
143 | var yDistanceSquared = 0;
144 |
145 | // [0, 1, -1, 2, -2, 3, -3, ...]
146 | for (int iY = 0; iY <= radiusInChunks * 2; ++iY) {
147 | int dChunkY = (iY & 1) != 0 ? iY - iY / 2 : iY / -2;
148 |
149 | if (dChunkY > 0)
150 | yDistanceSquared += 2 * dChunkY - 1;
151 |
152 | var yzDistanceSquared = yDistanceSquared;
153 |
154 | sliceLoop:
155 | for (var dChunkZ = 0; dChunkZ <= radiusInChunks; ++dChunkZ) {
156 | if (dChunkZ != 0)
157 | yzDistanceSquared += 2 * dChunkZ - 1;
158 |
159 | var yzxDistanceSquared = yzDistanceSquared;
160 | T priorGroupClosest = null;
161 |
162 | for (var dChunkX = 0; dChunkX <= dChunkZ; ++dChunkX) {
163 | if (dChunkX != 0)
164 | yzxDistanceSquared += 2 * dChunkX - 1;
165 |
166 | if (yzxDistanceSquared > maxDistanceSquared)
167 | break sliceLoop;
168 |
169 | if (closestSession.closestItem != null && yzxDistanceSquared > closestSession.closestChunkDistanceSquared + 2)
170 | break sliceLoop;
171 |
172 | closestSession.visitItems(dChunkX, dChunkY, dChunkZ);
173 |
174 | if (dChunkZ != 0)
175 | closestSession.visitItems(dChunkX, dChunkY, -dChunkZ);
176 |
177 | if (dChunkX != 0) {
178 | closestSession.visitItems(-dChunkX, dChunkY, dChunkZ);
179 | closestSession.visitItems(-dChunkX, dChunkY, -dChunkZ);
180 |
181 | if (dChunkX != dChunkZ) {
182 | closestSession.visitItems(dChunkZ, dChunkY, -dChunkX);
183 | closestSession.visitItems(-dChunkZ, dChunkY, -dChunkX);
184 | }
185 | }
186 |
187 | if (dChunkX != dChunkZ) {
188 | closestSession.visitItems(dChunkZ, dChunkY, dChunkX);
189 | closestSession.visitItems(-dChunkZ, dChunkY, dChunkX);
190 | }
191 |
192 | if (closestSession.closestItem == null)
193 | continue;
194 |
195 | if (priorGroupClosest == closestSession.closestItem)
196 | break;
197 |
198 | priorGroupClosest = closestSession.closestItem;
199 | }
200 | }
201 | }
202 |
203 | return closestSession.closestItem;
204 | }
205 |
206 | private int computeDistanceSquared(@Nullable Location itemLocation, Location origin) {
207 | if (itemLocation == null)
208 | return Integer.MAX_VALUE;
209 |
210 | return (
211 | square(origin.getBlockX() - itemLocation.getBlockX()) +
212 | square(origin.getBlockY() - itemLocation.getBlockY()) +
213 | square(origin.getBlockZ() - itemLocation.getBlockZ())
214 | );
215 | }
216 |
217 | private static int square(int value) {
218 | return value * value;
219 | }
220 |
221 | private static long fastChunkHash(Location location) {
222 | return fastChunkHash(
223 | location.getBlockX() >> 4,
224 | location.getBlockY() >> 4,
225 | location.getBlockZ() >> 4
226 | );
227 | }
228 |
229 | private static long fastChunkHash(int chunkX, int chunkY, int chunkZ) {
230 | // Assuming an axis is limited to roughly +- 30,000,000, that would be +- 1,875,000 chunks/axis
231 | // 2^(22-1) = 2,097,152 => 22 bits per axis should suffice
232 | // The y-axis does by far not exhaust this range, so it will be compromised to 20 bits
233 |
234 | // ---------- 22b ---------- ---------- 22b ---------- ---------- 20b ----------
235 | // 64.......................43 42.......................21 20.........................1
236 | // <1b sign chunkX><21b chunkX><1b sign chunkZ><21b chunkZ><1b sign chunkY><19b chunkY>
237 | return (
238 | (((((long) chunkX) & 0x3FFFFF) << (20 + 22)) | ((chunkX < 0 ? 1L : 0L) << (20 + 22 + 21))) |
239 | (((((long) chunkZ) & 0x3FFFFF) << 20) | ((chunkZ < 0 ? 1L : 0L) << (20 + 21))) |
240 | ((((long) chunkY) & 0x7FFFF) | ((chunkY < 0 ? 1L : 0L) << 19))
241 | );
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/integration/essentials_warps/EssentialsWarpData.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.essentials_warps;
2 |
3 | import org.bukkit.Location;
4 |
5 | public record EssentialsWarpData(
6 | String name,
7 | Location location
8 | ) {}
9 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/integration/essentials_warps/IEssentialsWarpsIntegration.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.essentials_warps;
2 |
3 | import org.bukkit.Location;
4 | import org.bukkit.entity.Player;
5 | import org.bukkit.event.Listener;
6 |
7 | import javax.annotation.Nullable;
8 |
9 | public interface IEssentialsWarpsIntegration extends Listener {
10 |
11 | EssentialsWarpData ESSENTIALS_WARP_NULL_SENTINEL = new EssentialsWarpData(null, null);
12 |
13 | @Nullable EssentialsWarpData locateNearestWithinRange(Player player, Location origin, int blockRadius);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/integration/player_warps/IPlayerWarpsIntegration.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.player_warps;
2 |
3 | import org.bukkit.Location;
4 | import org.bukkit.entity.Player;
5 | import org.bukkit.event.Listener;
6 |
7 | import javax.annotation.Nullable;
8 |
9 | public interface IPlayerWarpsIntegration extends Listener {
10 |
11 | PlayerWarpData PLAYER_WARP_NULL_SENTINEL = new PlayerWarpData(null, null, null, false);
12 |
13 | @Nullable PlayerWarpData locateNearestWithinRange(Player player, Location origin, int blockRadius);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/integration/player_warps/PlayerWarpData.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.player_warps;
2 |
3 | import org.bukkit.Location;
4 |
5 | public record PlayerWarpData(
6 | String ownerName,
7 | String warpName,
8 | Location location,
9 | boolean isBanned
10 | ) {}
--------------------------------------------------------------------------------
/common/src/main/java/me/blvckbytes/quick_shop_search/integration/worldguard/IWorldGuardIntegration.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.worldguard;
2 |
3 | import org.bukkit.Location;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.util.Collection;
7 | import java.util.List;
8 | import java.util.function.Predicate;
9 |
10 | public interface IWorldGuardIntegration {
11 |
12 | List getRegionsContainingLocation(Location location);
13 |
14 | static @Nullable Predicate makePredicate(
15 | Location origin,
16 | @Nullable IWorldGuardIntegration worldGuardIntegration,
17 | boolean withinSameRegion
18 | ) {
19 | if (worldGuardIntegration == null || !withinSameRegion)
20 | return null;
21 |
22 | var originRegions = worldGuardIntegration.getRegionsContainingLocation(origin);
23 |
24 | return candidateLocation -> {
25 | var candidateRegions = worldGuardIntegration.getRegionsContainingLocation(candidateLocation);
26 | return isRegionAllowed(originRegions, candidateRegions);
27 | };
28 | }
29 |
30 | static boolean isRegionAllowed(Collection allowedIds, Collection actualIds) {
31 | // There'll likely not be many regions at any given point, thus I believe that using
32 | // sets with their intersection-implementation will only slow things down unnecessarily.
33 | for (var actualRegion : actualIds) {
34 | for (var allowedRegion : allowedIds) {
35 | if (actualRegion.equals(allowedRegion))
36 | return true;
37 | }
38 | }
39 |
40 | return false;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/compatibility/qs_gt_6207_lte_6208/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | me.blvckbytes
8 | quickshopsearch-root
9 | ${revision}
10 | ../../pom.xml
11 |
12 |
13 | qs_gt_6207_lte_6208
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 |
23 | com.ghostchu
24 | quickshop-bukkit
25 | 6.2.0.8
26 | provided
27 |
28 |
29 | com.ghostchu
30 | quickshop-api
31 | 6.2.0.8
32 | provided
33 |
34 |
35 | me.blvckbytes
36 | common
37 | ${revision}
38 |
39 |
40 |
41 | com.ghostchu
42 | simplereloadlib
43 | 1.1.2
44 | provided
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/compatibility/qs_gt_6207_lte_6208/src/main/java/me/blvckbytes/quick_shop_search/compatibility/QuickShopListener_GT_6207_LTE_6208.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.compatibility;
2 |
3 | import com.ghostchu.quickshop.api.event.details.*;
4 | import com.ghostchu.quickshop.api.event.economy.ShopSuccessPurchaseEvent;
5 | import com.ghostchu.quickshop.api.event.general.ShopSignUpdateEvent;
6 | import com.ghostchu.quickshop.api.event.inventory.ShopInventoryCalculateEvent;
7 | import com.ghostchu.quickshop.api.event.modification.ShopCreateSuccessEvent;
8 | import com.ghostchu.quickshop.api.event.modification.ShopDeleteEvent;
9 | import me.blvckbytes.quick_shop_search.compatibility.QuickShopEventConsumer;
10 | import org.bukkit.event.EventHandler;
11 | import org.bukkit.event.Listener;
12 |
13 | public class QuickShopListener_GT_6207_LTE_6208 implements Listener {
14 |
15 | private final QuickShopEventConsumer consumer;
16 |
17 | public QuickShopListener_GT_6207_LTE_6208(QuickShopEventConsumer consumer) {
18 | this.consumer = consumer;
19 | }
20 |
21 | @EventHandler
22 | public void onPurchaseSuccess(ShopSuccessPurchaseEvent event) {
23 | consumer.onPurchaseSuccess(event.getShop(), event.getAmount(), event.getPurchaser().getUniqueId());
24 | }
25 |
26 | @EventHandler
27 | public void onShopCreate(ShopCreateSuccessEvent event) {
28 | consumer.onShopCreate(event.getShop());
29 | }
30 |
31 | @EventHandler
32 | public void onShopDelete(ShopDeleteEvent event) {
33 | if (event.isCancelled())
34 | return;
35 |
36 | consumer.onShopDelete(event.getShop());
37 | }
38 |
39 | @EventHandler
40 | public void onShopItemChange(ShopItemChangeEvent event) {
41 | if (event.isCancelled())
42 | return;
43 |
44 | consumer.onShopItemChange(event.getShop(), event.getNewItem());
45 | }
46 |
47 | @EventHandler
48 | public void onShopOwnerChange(ShopOwnershipTransferEvent event) {
49 | if (event.isCancelled())
50 | return;
51 |
52 | consumer.onShopOwnerChange(event.getShop());
53 | }
54 |
55 | @EventHandler
56 | public void onShopSignUpdate(ShopSignUpdateEvent event) {
57 | consumer.onShopSignUpdate(event.getShop());
58 | }
59 |
60 | @EventHandler
61 | public void onShopInventoryCalculate(ShopInventoryCalculateEvent event) {
62 | consumer.onShopInventoryCalculate(event.getShop(), event.getStock(), event.getSpace());
63 | }
64 |
65 | @EventHandler
66 | public void onShopNameChange(ShopNamingEvent event) {
67 | if (event.isCancelled())
68 | return;
69 |
70 | consumer.onShopNameChange(event.getShop());
71 | }
72 |
73 | @EventHandler
74 | public void onShopPriceChange(ShopPriceChangeEvent event) {
75 | if (event.isCancelled())
76 | return;
77 |
78 | consumer.onShopPriceChange(event.getShop());
79 | }
80 |
81 | @EventHandler
82 | public void onShopTypeChange(ShopTypeChangeEvent event) {
83 | if (event.isCancelled())
84 | return;
85 |
86 | consumer.onShopTypeChange(event.getShop());
87 | }
88 |
89 | @EventHandler
90 | public void onShopUnlimitedChange(ShopUnlimitedStatusEvent event) {
91 | if (event.isCancelled())
92 | return;
93 |
94 | consumer.onShopTypeChange(event.getShop());
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/compatibility/qs_gt_6207_lte_6208/src/main/java/me/blvckbytes/quick_shop_search/compatibility/RemoteInteractionApi_GT_6207_LTE_6208.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.compatibility;
2 |
3 | import com.ghostchu.quickshop.QuickShop;
4 | import com.ghostchu.quickshop.api.QuickShopAPI;
5 | import com.ghostchu.quickshop.api.shop.Shop;
6 | import com.ghostchu.quickshop.api.shop.ShopAction;
7 | import com.ghostchu.quickshop.obj.QUserImpl;
8 | import com.ghostchu.quickshop.shop.SimpleInfo;
9 | import com.ghostchu.quickshop.shop.inventory.BukkitInventoryWrapper;
10 | import me.blvckbytes.quick_shop_search.compatibility.RemoteInteractionApi;
11 | import org.bukkit.entity.Player;
12 |
13 | public class RemoteInteractionApi_GT_6207_LTE_6208 implements RemoteInteractionApi {
14 |
15 | @Override
16 | public void interact(Player player, Shop shop, int amount) {
17 | var tradeInfo = new SimpleInfo(
18 | shop.getLocation(),
19 | shop.isBuying() ? ShopAction.PURCHASE_SELL : ShopAction.PURCHASE_BUY,
20 | null, null, shop, false
21 | );
22 |
23 | var wrappedInventory = new BukkitInventoryWrapper(player.getInventory());
24 |
25 | if (shop.isBuying()) {
26 | QuickShopAPI.getInstance().getShopManager().actionBuying(
27 | player, wrappedInventory,
28 | QuickShop.getInstance().getEconomy(),
29 | tradeInfo, shop, amount
30 | );
31 | } else {
32 | QuickShopAPI.getInstance().getShopManager().actionSelling(
33 | player, wrappedInventory,
34 | QuickShop.getInstance().getEconomy(),
35 | tradeInfo, shop, amount
36 | );
37 | }
38 | }
39 |
40 | @Override
41 | public double getPlayerBalance(Player player, Shop shop) {
42 | var shopLocation = shop.getLocation();
43 | var shopWorld = shopLocation.getWorld();
44 |
45 | if (shopWorld == null)
46 | return 0;
47 |
48 | return QuickShop.getInstance().getEconomy()
49 | .getBalance(QUserImpl.createFullFilled(player), shopWorld, shop.getCurrency());
50 | }
51 |
52 | @Override
53 | public double getOwnerBalance(Shop shop) {
54 | var shopLocation = shop.getLocation();
55 | var shopWorld = shopLocation.getWorld();
56 |
57 | if (shopWorld == null)
58 | return 0;
59 |
60 | return QuickShop.getInstance().getEconomy()
61 | .getBalance(shop.getOwner(), shopWorld, shop.getCurrency());
62 | }
63 |
64 | @Override
65 | public boolean withdrawAmount(Player player, Shop shop, double amount) {
66 | var shopLocation = shop.getLocation();
67 | var shopWorld = shopLocation.getWorld();
68 |
69 | if (shopWorld == null)
70 | return false;
71 |
72 | return QuickShop.getInstance().getEconomy()
73 | .withdraw(QUserImpl.createFullFilled(player), amount, shopWorld, shop.getCurrency());
74 | }
75 |
76 | @Override
77 | public boolean depositAmount(Player player, Shop shop, double amount) {
78 | var shopLocation = shop.getLocation();
79 | var shopWorld = shopLocation.getWorld();
80 |
81 | if (shopWorld == null)
82 | return false;
83 |
84 | return QuickShop.getInstance().getEconomy()
85 | .deposit(QUserImpl.createFullFilled(player), amount, shopWorld, shop.getCurrency());
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/compatibility/qs_gt_6208/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | me.blvckbytes
8 | quickshopsearch-root
9 | ${revision}
10 | ../../pom.xml
11 |
12 |
13 | qs_gt_6208
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 |
23 | com.ghostchu
24 | quickshop-bukkit
25 | 6.2.0.9-RELEASE-1
26 | provided
27 |
28 |
29 | com.ghostchu
30 | quickshop-api
31 | 6.2.0.9-RELEASE-1
32 | provided
33 |
34 |
35 | me.blvckbytes
36 | common
37 | ${revision}
38 |
39 |
40 |
41 | com.ghostchu
42 | simplereloadlib
43 | 1.1.2
44 | provided
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/compatibility/qs_gt_6208/src/main/java/me/blvckbytes/quick_shop_search/compatibility/QuickShopListener_GT_6208.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.compatibility;
2 |
3 | import com.ghostchu.quickshop.api.event.Phase;
4 | import com.ghostchu.quickshop.api.event.economy.ShopSuccessPurchaseEvent;
5 | import com.ghostchu.quickshop.api.event.general.ShopSignUpdateEvent;
6 | import com.ghostchu.quickshop.api.event.inventory.ShopInventoryCalculateEvent;
7 | import com.ghostchu.quickshop.api.event.management.ShopCreateEvent;
8 | import com.ghostchu.quickshop.api.event.management.ShopDeleteEvent;
9 | import com.ghostchu.quickshop.api.event.settings.type.*;
10 | import me.blvckbytes.quick_shop_search.compatibility.QuickShopEventConsumer;
11 | import org.bukkit.event.EventHandler;
12 | import org.bukkit.event.Listener;
13 |
14 | public class QuickShopListener_GT_6208 implements Listener {
15 |
16 | private final QuickShopEventConsumer consumer;
17 |
18 | public QuickShopListener_GT_6208(QuickShopEventConsumer consumer) {
19 | this.consumer = consumer;
20 | }
21 |
22 | @EventHandler
23 | public void onPurchaseSuccess(ShopSuccessPurchaseEvent event) {
24 | consumer.onPurchaseSuccess(event.getShop(), event.getAmount(), event.getPurchaser().getUniqueId());
25 | }
26 |
27 | @EventHandler
28 | public void onShopCreate(ShopCreateEvent event) {
29 | if (event.isCancelled() || event.phase() != Phase.POST)
30 | return;
31 |
32 | event.shop().ifPresent(consumer::onShopCreate);
33 | }
34 |
35 | @EventHandler
36 | public void onShopDelete(ShopDeleteEvent event) {
37 | if (event.isCancelled() || event.phase() != Phase.POST)
38 | return;
39 |
40 | event.shop().ifPresent(consumer::onShopDelete);
41 | }
42 |
43 | @EventHandler
44 | public void onShopItemChange(ShopItemEvent event) {
45 | if (event.isCancelled() || event.phase() != Phase.POST)
46 | return;
47 |
48 | consumer.onShopItemChange(event.shop(), event.updated());
49 | }
50 |
51 | @EventHandler
52 | public void onShopOwnerChange(ShopOwnerEvent event) {
53 | if (event.isCancelled() || event.phase() != Phase.POST)
54 | return;
55 |
56 | consumer.onShopOwnerChange(event.shop());
57 | }
58 |
59 | @EventHandler
60 | public void onShopSignUpdate(ShopSignUpdateEvent event) {
61 | consumer.onShopSignUpdate(event.getShop());
62 | }
63 |
64 | @EventHandler
65 | public void onShopInventoryCalculate(ShopInventoryCalculateEvent event) {
66 | consumer.onShopInventoryCalculate(event.getShop(), event.getStock(), event.getSpace());
67 | }
68 |
69 | @EventHandler
70 | public void onShopNameChange(ShopNameEvent event) {
71 | if (event.isCancelled() || event.phase() != Phase.POST)
72 | return;
73 |
74 | consumer.onShopNameChange(event.shop());
75 | }
76 |
77 | @EventHandler
78 | public void onShopPriceChange(ShopPriceEvent event) {
79 | if (event.isCancelled() || event.phase() != Phase.POST)
80 | return;
81 |
82 | consumer.onShopPriceChange(event.shop());
83 | }
84 |
85 | @EventHandler
86 | public void onShopTypeChange(ShopTypeEvent event) {
87 | if (event.isCancelled() || event.phase() != Phase.POST)
88 | return;
89 |
90 | consumer.onShopTypeChange(event.shop());
91 | }
92 |
93 | @EventHandler
94 | public void onShopUnlimitedChange(ShopUnlimitedEvent event) {
95 | if (event.isCancelled() || event.phase() != Phase.POST)
96 | return;
97 |
98 | consumer.onShopTypeChange(event.shop());
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/compatibility/qs_gt_6208/src/main/java/me/blvckbytes/quick_shop_search/compatibility/RemoteInteractionApi_GT_6208.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.compatibility;
2 |
3 | import com.ghostchu.quickshop.QuickShop;
4 | import com.ghostchu.quickshop.api.QuickShopAPI;
5 | import com.ghostchu.quickshop.api.shop.Shop;
6 | import com.ghostchu.quickshop.api.shop.ShopAction;
7 | import com.ghostchu.quickshop.obj.QUserImpl;
8 | import com.ghostchu.quickshop.shop.SimpleInfo;
9 | import com.ghostchu.quickshop.shop.inventory.BukkitInventoryWrapper;
10 | import me.blvckbytes.quick_shop_search.compatibility.RemoteInteractionApi;
11 | import org.bukkit.entity.Player;
12 |
13 | public class RemoteInteractionApi_GT_6208 implements RemoteInteractionApi {
14 |
15 | @Override
16 | public void interact(Player player, Shop shop, int amount) {
17 | var tradeInfo = new SimpleInfo(
18 | shop.getLocation(),
19 | shop.isBuying() ? ShopAction.PURCHASE_SELL : ShopAction.PURCHASE_BUY,
20 | null, null, shop, false
21 | );
22 |
23 | var wrappedInventory = new BukkitInventoryWrapper(player.getInventory());
24 |
25 | if (shop.isBuying()) {
26 | QuickShopAPI.getInstance().getShopManager().actionBuying(
27 | player, wrappedInventory,
28 | QuickShop.getInstance().getEconomy(),
29 | tradeInfo, shop, amount
30 | );
31 | } else {
32 | QuickShopAPI.getInstance().getShopManager().actionSelling(
33 | player, wrappedInventory,
34 | QuickShop.getInstance().getEconomy(),
35 | tradeInfo, shop, amount
36 | );
37 | }
38 | }
39 |
40 | @Override
41 | public double getPlayerBalance(Player player, Shop shop) {
42 | var shopLocation = shop.getLocation();
43 | var shopWorld = shopLocation.getWorld();
44 |
45 | if (shopWorld == null)
46 | return 0;
47 |
48 | return QuickShop.getInstance().getEconomy()
49 | .getBalance(QUserImpl.createFullFilled(player), shopWorld, shop.getCurrency());
50 | }
51 |
52 | @Override
53 | public double getOwnerBalance(Shop shop) {
54 | var shopLocation = shop.getLocation();
55 | var shopWorld = shopLocation.getWorld();
56 |
57 | if (shopWorld == null)
58 | return 0;
59 |
60 | return QuickShop.getInstance().getEconomy()
61 | .getBalance(shop.getOwner(), shopWorld, shop.getCurrency());
62 | }
63 |
64 | @Override
65 | public boolean withdrawAmount(Player player, Shop shop, double amount) {
66 | var shopLocation = shop.getLocation();
67 | var shopWorld = shopLocation.getWorld();
68 |
69 | if (shopWorld == null)
70 | return false;
71 |
72 | return QuickShop.getInstance().getEconomy()
73 | .withdraw(QUserImpl.createFullFilled(player), amount, shopWorld, shop.getCurrency());
74 | }
75 |
76 | @Override
77 | public boolean depositAmount(Player player, Shop shop, double amount) {
78 | var shopLocation = shop.getLocation();
79 | var shopWorld = shopLocation.getWorld();
80 |
81 | if (shopWorld == null)
82 | return false;
83 |
84 | return QuickShop.getInstance().getEconomy()
85 | .deposit(QUserImpl.createFullFilled(player), amount, shopWorld, shop.getCurrency());
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/compatibility/qs_lte_6207/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | me.blvckbytes
8 | quickshopsearch-root
9 | ${revision}
10 | ../../pom.xml
11 |
12 |
13 | qs_lte_6207
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 |
23 | com.ghostchu
24 | quickshop-bukkit
25 | 6.2.0.6
26 | provided
27 |
28 |
29 | com.ghostchu
30 | quickshop-api
31 | 6.2.0.6
32 | provided
33 |
34 |
35 | me.blvckbytes
36 | common
37 | ${revision}
38 |
39 |
40 |
41 | com.ghostchu
42 | simplereloadlib
43 | 1.1.2
44 | provided
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/compatibility/qs_lte_6207/src/main/java/me/blvckbytes/quick_shop_search/compatibility/QuickShopListener_LTE_6207.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.compatibility;
2 |
3 | import com.ghostchu.quickshop.api.event.*;
4 | import me.blvckbytes.quick_shop_search.compatibility.QuickShopEventConsumer;
5 | import org.bukkit.event.EventHandler;
6 | import org.bukkit.event.Listener;
7 |
8 | public class QuickShopListener_LTE_6207 implements Listener {
9 |
10 | private final QuickShopEventConsumer consumer;
11 |
12 | public QuickShopListener_LTE_6207(QuickShopEventConsumer consumer) {
13 | this.consumer = consumer;
14 | }
15 |
16 | @EventHandler
17 | public void onPurchaseSuccess(ShopSuccessPurchaseEvent event) {
18 | consumer.onPurchaseSuccess(event.getShop(), event.getAmount(), event.getPurchaser().getUniqueId());
19 | }
20 |
21 | @EventHandler
22 | public void onShopCreate(ShopCreateSuccessEvent event) {
23 | consumer.onShopCreate(event.getShop());
24 | }
25 |
26 | @EventHandler
27 | public void onShopDelete(ShopDeleteEvent event) {
28 | if (event.isCancelled())
29 | return;
30 |
31 | consumer.onShopDelete(event.getShop());
32 | }
33 |
34 | @EventHandler
35 | public void onShopItemChange(ShopItemChangeEvent event) {
36 | if (event.isCancelled())
37 | return;
38 |
39 | consumer.onShopItemChange(event.getShop(), event.getNewItem());
40 | }
41 |
42 | @EventHandler
43 | public void onShopOwnerChange(ShopOwnershipTransferEvent event) {
44 | if (event.isCancelled())
45 | return;
46 |
47 | consumer.onShopOwnerChange(event.getShop());
48 | }
49 |
50 | @EventHandler
51 | public void onShopSignUpdate(ShopSignUpdateEvent event) {
52 | consumer.onShopSignUpdate(event.getShop());
53 | }
54 |
55 | @EventHandler
56 | public void onShopInventoryCalculate(ShopInventoryCalculateEvent event) {
57 | consumer.onShopInventoryCalculate(event.getShop(), event.getStock(), event.getSpace());
58 | }
59 |
60 | @EventHandler
61 | public void onShopNameChange(ShopNamingEvent event) {
62 | if (event.isCancelled())
63 | return;
64 |
65 | consumer.onShopNameChange(event.getShop());
66 | }
67 |
68 | @EventHandler
69 | public void onShopPriceChange(ShopPriceChangeEvent event) {
70 | if (event.isCancelled())
71 | return;
72 |
73 | consumer.onShopPriceChange(event.getShop());
74 | }
75 |
76 | @EventHandler
77 | public void onShopTypeChange(ShopTypeChangeEvent event) {
78 | if (event.isCancelled())
79 | return;
80 |
81 | consumer.onShopTypeChange(event.getShop());
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/compatibility/qs_lte_6207/src/main/java/me/blvckbytes/quick_shop_search/compatibility/RemoteInteractionApi_LTE_6207.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.compatibility;
2 |
3 | import com.ghostchu.quickshop.QuickShop;
4 | import com.ghostchu.quickshop.api.QuickShopAPI;
5 | import com.ghostchu.quickshop.api.shop.Shop;
6 | import com.ghostchu.quickshop.api.shop.ShopAction;
7 | import com.ghostchu.quickshop.api.shop.ShopManager;
8 | import com.ghostchu.quickshop.obj.QUserImpl;
9 | import com.ghostchu.quickshop.shop.SimpleInfo;
10 | import com.ghostchu.quickshop.shop.inventory.BukkitInventoryWrapper;
11 | import me.blvckbytes.quick_shop_search.compatibility.RemoteInteractionApi;
12 | import org.bukkit.entity.Player;
13 |
14 | import java.lang.reflect.Modifier;
15 |
16 | public class RemoteInteractionApi_LTE_6207 implements RemoteInteractionApi {
17 |
18 | private final ShopManager shopManager;
19 |
20 | public RemoteInteractionApi_LTE_6207() {
21 | this.shopManager = QuickShopAPI.getInstance().getShopManager();
22 |
23 | for (var method : shopManager.getClass().getDeclaredMethods()) {
24 | if (Modifier.isStatic(method.getModifiers()))
25 | continue;
26 |
27 | if (!Modifier.isPublic(method.getModifiers()))
28 | continue;
29 |
30 | if (method.getName().equals("actionBuying") && method.getReturnType() != Void.class)
31 | throw new IllegalStateException("Expected actionBuying to have no return-value");
32 |
33 | if (method.getName().equals("actionSelling") && method.getReturnType() != Void.class)
34 | throw new IllegalStateException("Expected actionSelling to have no return-value");
35 | }
36 | }
37 |
38 | @Override
39 | public void interact(Player player, Shop shop, int amount) {
40 | var tradeInfo = new SimpleInfo(
41 | shop.getLocation(),
42 | shop.isBuying() ? ShopAction.PURCHASE_SELL : ShopAction.PURCHASE_BUY,
43 | null, null, shop, false
44 | );
45 |
46 | var wrappedInventory = new BukkitInventoryWrapper(player.getInventory());
47 |
48 | if (shop.isBuying()) {
49 | shopManager.actionBuying(
50 | player, wrappedInventory,
51 | QuickShop.getInstance().getEconomy(),
52 | tradeInfo, shop, amount
53 | );
54 | } else {
55 | shopManager.actionSelling(
56 | player, wrappedInventory,
57 | QuickShop.getInstance().getEconomy(),
58 | tradeInfo, shop, amount
59 | );
60 | }
61 | }
62 |
63 | @Override
64 | public double getPlayerBalance(Player player, Shop shop) {
65 | var shopLocation = shop.getLocation();
66 | var shopWorld = shopLocation.getWorld();
67 |
68 | if (shopWorld == null)
69 | return 0;
70 |
71 | return QuickShop.getInstance().getEconomy()
72 | .getBalance(QUserImpl.createFullFilled(player), shopWorld, shop.getCurrency());
73 | }
74 |
75 | @Override
76 | public double getOwnerBalance(Shop shop) {
77 | var shopLocation = shop.getLocation();
78 | var shopWorld = shopLocation.getWorld();
79 |
80 | if (shopWorld == null)
81 | return 0;
82 |
83 | return QuickShop.getInstance().getEconomy()
84 | .getBalance(shop.getOwner(), shopWorld, shop.getCurrency());
85 | }
86 |
87 | @Override
88 | public boolean withdrawAmount(Player player, Shop shop, double amount) {
89 | var shopLocation = shop.getLocation();
90 | var shopWorld = shopLocation.getWorld();
91 |
92 | if (shopWorld == null)
93 | return false;
94 |
95 | return QuickShop.getInstance().getEconomy()
96 | .withdraw(QUserImpl.createFullFilled(player), amount, shopWorld, shop.getCurrency());
97 | }
98 |
99 | @Override
100 | public boolean depositAmount(Player player, Shop shop, double amount) {
101 | var shopLocation = shop.getLocation();
102 | var shopWorld = shopLocation.getWorld();
103 |
104 | if (shopWorld == null)
105 | return false;
106 |
107 | return QuickShop.getInstance().getEconomy()
108 | .deposit(QUserImpl.createFullFilled(player), amount, shopWorld, shop.getCurrency());
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | me.blvckbytes
8 | quickshopsearch-root
9 | ${revision}
10 |
11 |
12 | core
13 | QuickShopSearch
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 |
23 | net.kyori
24 | adventure-api
25 | 4.17.0
26 |
27 |
28 | com.ghostchu
29 | quickshop-api
30 | 6.2.0.6
31 | provided
32 |
33 |
34 | com.ghostchu
35 | quickshop-bukkit
36 | 6.2.0.6
37 | provided
38 |
39 |
40 |
41 | com.ghostchu
42 | simplereloadlib
43 | 1.1.2
44 | provided
45 |
46 |
47 | me.blvckbytes
48 | common
49 | ${revision}
50 |
51 |
52 | me.blvckbytes
53 | qs_gt_6207_lte_6208
54 | ${revision}
55 |
56 |
57 | me.blvckbytes
58 | qs_lte_6207
59 | ${revision}
60 |
61 |
62 | me.blvckbytes
63 | qs_gt_6208
64 | ${revision}
65 |
66 |
67 | org.yaml
68 | snakeyaml
69 | 2.3
70 |
71 |
72 | me.blvckbytes
73 | olziedev_player_warps_integration
74 | ${revision}
75 |
76 |
77 | me.blvckbytes
78 | revivalo_player_warps_integration
79 | ${revision}
80 |
81 |
82 | me.blvckbytes
83 | essentials_warps_integration
84 | ${revision}
85 |
86 |
87 | me.blvckbytes
88 | worldguard_integration
89 | ${revision}
90 |
91 |
92 |
93 |
94 |
95 |
96 | org.apache.maven.plugins
97 | maven-shade-plugin
98 | 3.6.0
99 |
100 |
101 | package
102 |
103 | shade
104 |
105 |
106 |
107 |
108 | ${project.name}-${project.version}
109 |
110 |
111 |
112 |
113 | me.blvckbytes:BBConfigMapper
114 | me.blvckbytes:BukkitEvaluable
115 | me.blvckbytes:SyllablesMatcher
116 | me.blvckbytes:GPEEE
117 | me.blvckbytes:common
118 | me.blvckbytes:qs_lte_6207
119 | me.blvckbytes:qs_gt_6207_lte_6208
120 | me.blvckbytes:qs_gt_6208
121 | me.blvckbytes:olziedev_player_warps_integration
122 | me.blvckbytes:revivalo_player_warps_integration
123 | me.blvckbytes:essentials_warps_integration
124 | me.blvckbytes:worldguard_integration
125 | com.github.technicallycoded:FoliaLib
126 |
127 | org.yaml:snakeyaml
128 | com.github.cryptomorin:XSeries
129 |
130 |
131 |
132 |
133 |
134 |
135 | me/blvckbytes/(bbconfigmapper|bukkitevaluable|gpeee|syllables_matcher)/(.*)
136 | me/blvckbytes/quick_shop_search/$1/$2
137 | true
138 |
139 |
140 |
141 | (com/cryptomorin/xseries)
142 | me/blvckbytes/quick_shop_search/$1
143 | true
144 |
145 |
146 |
147 | (com/tcoded/folialib)
148 | me/blvckbytes/quick_shop_search/$1
149 | true
150 |
151 |
152 |
153 |
154 | org/yaml/snakeyaml/(.*)
155 | me/blvckbytes/quick_shop_search/org/yaml/snakeyaml/$1
156 | true
157 |
158 |
159 |
160 |
161 |
162 | *:*
163 |
164 | META-INF/license/**
165 | META-INF/*
166 | META-INF/maven/**
167 | LICENSE
168 | NOTICE
169 | /*.txt
170 | build.properties
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/ChatPromptInstance.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search;
2 |
3 | import com.tcoded.folialib.wrapper.task.WrappedTask;
4 |
5 | import java.util.function.Consumer;
6 |
7 | public record ChatPromptInstance(
8 | WrappedTask timeoutTask,
9 | Consumer handler
10 | ) {}
11 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/ChatPromptManager.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 | import me.blvckbytes.item_predicate_parser.translation.resolver.TranslationResolver;
5 | import org.bukkit.entity.Player;
6 | import org.bukkit.event.EventHandler;
7 | import org.bukkit.event.EventPriority;
8 | import org.bukkit.event.Listener;
9 | import org.bukkit.event.player.AsyncPlayerChatEvent;
10 | import org.bukkit.event.player.PlayerQuitEvent;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | import java.util.HashMap;
14 | import java.util.Map;
15 | import java.util.UUID;
16 | import java.util.function.Consumer;
17 |
18 | public class ChatPromptManager implements Listener {
19 |
20 | private static final int TIMEOUT_DURATION_S = 15;
21 |
22 | private final Map handlerByUuid;
23 | private final PlatformScheduler scheduler;
24 |
25 | public ChatPromptManager(PlatformScheduler scheduler) {
26 | this.handlerByUuid = new HashMap<>();
27 | this.scheduler = scheduler;
28 | }
29 |
30 | /**
31 | * @return True if a previous handler has been overwritten
32 | */
33 | public boolean register(Player player, Consumer inputHandler, Runnable timeoutHandler) {
34 | synchronized (handlerByUuid) {
35 | var existed = removeAndCancelIfExists(player) != null;
36 | var playerId = player.getUniqueId();
37 |
38 | var removalTask = scheduler.runLater(() -> {
39 | synchronized (handlerByUuid) {
40 | if (handlerByUuid.remove(playerId) != null)
41 | timeoutHandler.run();
42 | }
43 | }, TIMEOUT_DURATION_S * 20L);
44 |
45 | handlerByUuid.put(playerId, new ChatPromptInstance(removalTask, inputHandler));
46 |
47 | return existed;
48 | }
49 | }
50 |
51 | @EventHandler(priority = EventPriority.LOWEST)
52 | public void onChat(AsyncPlayerChatEvent event) {
53 | ChatPromptInstance handler;
54 |
55 | synchronized (handlerByUuid) {
56 | handler = removeAndCancelIfExists(event.getPlayer());
57 | }
58 |
59 | // Might not be the most decoupled solution, but since QSS depends on IPP, we're fine.
60 | var sanitizedMessage = TranslationResolver.sanitize(event.getMessage());
61 |
62 | if (handler != null) {
63 | event.setCancelled(true);
64 | handler.handler().accept(sanitizedMessage);
65 | }
66 | }
67 |
68 | @EventHandler
69 | public void onQuit(PlayerQuitEvent event) {
70 | synchronized (handlerByUuid) {
71 | removeAndCancelIfExists(event.getPlayer());
72 | }
73 | }
74 |
75 | private @Nullable ChatPromptInstance removeAndCancelIfExists(Player player) {
76 | var handler = handlerByUuid.remove(player.getUniqueId());
77 |
78 | if (handler != null)
79 | handler.timeoutTask().cancel();
80 |
81 | return handler;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/OfflinePlayerRegistry.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.OfflinePlayer;
5 | import org.bukkit.event.EventHandler;
6 | import org.bukkit.event.Listener;
7 | import org.bukkit.event.player.PlayerJoinEvent;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | import java.util.*;
11 |
12 | public class OfflinePlayerRegistry implements Listener {
13 |
14 | private record NameEntry(
15 | String name,
16 | String nameLower
17 | ) {}
18 |
19 | private final Set knownPlayerNamesLower;
20 | private final Map offlinePlayerByNameLower;
21 |
22 | public OfflinePlayerRegistry() {
23 | this.knownPlayerNamesLower = new HashSet<>();
24 | this.offlinePlayerByNameLower = new HashMap<>();
25 |
26 | for (var offlinePlayer : Bukkit.getOfflinePlayers())
27 | registerName(offlinePlayer);
28 | }
29 |
30 | @EventHandler
31 | public void onJoin(PlayerJoinEvent event) {
32 | registerName(event.getPlayer());
33 | }
34 |
35 | public @Nullable OfflinePlayer getByName(String name) {
36 | return offlinePlayerByNameLower.get(name.toLowerCase());
37 | }
38 |
39 | public List createSuggestions(String input, int limit) {
40 | var result = new ArrayList();
41 | var lowerInput = input.toLowerCase();
42 |
43 | for (var playerName : knownPlayerNamesLower) {
44 | if (!playerName.nameLower.startsWith(lowerInput))
45 | continue;
46 |
47 | result.add(playerName.name);
48 |
49 | if (result.size() == limit)
50 | break;
51 | }
52 |
53 | return result;
54 | }
55 |
56 | private void registerName(OfflinePlayer player) {
57 | var name = player.getName();
58 |
59 | if (name == null)
60 | return;
61 |
62 | var nameLower = name.toLowerCase();
63 |
64 | this.knownPlayerNamesLower.add(new NameEntry(name, nameLower));
65 | this.offlinePlayerByNameLower.put(nameLower, player);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/PluginPermission.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search;
2 |
3 | import org.bukkit.command.CommandSender;
4 | import org.bukkit.command.ConsoleCommandSender;
5 | import org.bukkit.entity.Player;
6 |
7 | public enum PluginPermission {
8 | MAIN_COMMAND("command.qss"),
9 | LANGUAGE_COMMAND("command.qssl"),
10 | RELOAD_COMMAND("command.qssrl"),
11 | ADVERTISE_COMMAND("command.advertise"),
12 | ADVERTISE_COMMAND_OWNER_BYPASS("command.advertise.owner-bypass"),
13 | ADVERTISE_COMMAND_ALLOWLIST_BYPASS("command.advertise.allowlist-bypass"),
14 | ADVERTISE_COMMAND_ALLOWLIST_BYPASS_OTHERS("command.advertise.allowlist-bypass.others"),
15 | EMPTY_PREDICATE("empty-predicate"),
16 | FEATURE_SORT("feature.sort"),
17 | FEATURE_FILTER("feature.filter"),
18 | FEATURE_TELEPORT("feature.teleport"),
19 | FEATURE_TELEPORT_NEAREST_PLAYER_WARP_BAN_BYPASS("feature.teleport.nearest-player-warp.ban-bypass"),
20 | FEATURE_TELEPORT_BYPASS_COOLDOWN_SAME_SHOP("feature.teleport.bypass-cooldown.same-shop"),
21 | FEATURE_TELEPORT_BYPASS_COOLDOWN_ANY_SHOP("feature.teleport.bypass-cooldown.any-shop"),
22 | FEATURE_TELEPORT_OTHER_WORLD("feature.teleport.other-world"),
23 | FEATURE_TELEPORT_OTHER_WORLD_BYPASS_COOLDOWN_SAME_SHOP("feature.teleport.other-world.bypass-cooldown.same-shop"),
24 | FEATURE_TELEPORT_OTHER_WORLD_BYPASS_COOLDOWN_ANY_SHOP("feature.teleport.other-world.bypass-cooldown.any-shop"),
25 | FEATURE_INTERACT("feature.interact"),
26 | FEATURE_INTERACT_OTHER_WORLD("feature.interact.other-world"),
27 | FEATURE_LIVE_UPDATES("feature.live-updates"),
28 | FEATURE_SEARCH_FLAG_OWNER("feature.search-flag.owner"),
29 | FEATURE_SEARCH_FLAG_RADIUS("feature.search-flag.radius"),
30 | FEATURE_SEARCH_FLAG_PRICE("feature.search-flag.price"),
31 | FEATURE_SEARCH_FLAG_MIN_PRICE("feature.search-flag.min-price"),
32 | FEATURE_SEARCH_FLAG_MAX_PRICE("feature.search-flag.max-price"),
33 | FEATURE_FEES_BYPASS("feature.fees.bypass"),
34 | FEATURE_FEES_BYPASS_OTHER_WORLD("feature.fees.bypass.other-world"),
35 | FEATURE_FEES_PERMISSION_NAME_BASE("feature.fees.permission-name"),
36 | OTHER_WORLD("other-world"),
37 | NON_ADVERTISE_BYPASS("bypass-non-advertise"),
38 | ACCESS_LIST_BASE("access-list"),
39 | ACCESS_LISTS_BYPASS("bypass-access-lists"),
40 | TELEPORT_COOLDOWN_GROUP_BASE("teleport-cooldown"),
41 | SLOW_TELEPORT_BYPASS("bypass-slow-teleport")
42 | ;
43 |
44 | private static final String PREFIX = "quickshopsearch";
45 | public final String node;
46 |
47 | PluginPermission(String node) {
48 | this.node = PREFIX + "." + node;
49 | }
50 |
51 | public String nodeWithSuffix(String suffix) {
52 | var nodeTrailing = node.charAt(node.length() - 1) == '.';
53 | var suffixLeading = suffix.charAt(0) == '.';
54 |
55 | if (nodeTrailing && suffixLeading)
56 | return node + suffix.substring(1);
57 |
58 | if (!nodeTrailing && !suffixLeading)
59 | return node + "." + suffix;
60 |
61 | return node + suffix;
62 | }
63 |
64 | public boolean has(CommandSender sender) {
65 | if (sender instanceof ConsoleCommandSender)
66 | return true;
67 |
68 | if (sender instanceof Player player)
69 | return player.hasPermission(node);
70 |
71 | return false;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/QuickShopSearchPlugin.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search;
2 |
3 | import com.cryptomorin.xseries.XMaterial;
4 | import com.ghostchu.quickshop.QuickShop;
5 | import com.ghostchu.quickshop.api.command.CommandContainer;
6 | import com.tcoded.folialib.FoliaLib;
7 | import me.blvckbytes.bbconfigmapper.ScalarType;
8 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
9 | import me.blvckbytes.bukkitevaluable.CommandUpdater;
10 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
11 | import me.blvckbytes.bukkitevaluable.ConfigManager;
12 | import me.blvckbytes.item_predicate_parser.ItemPredicateParserPlugin;
13 | import me.blvckbytes.quick_shop_search.cache.CachedShopRegistry;
14 | import me.blvckbytes.quick_shop_search.cache.QuickShopVersionDependentFactory;
15 | import me.blvckbytes.quick_shop_search.command.CommandSendListener;
16 | import me.blvckbytes.quick_shop_search.command.QuickShopSearchCommand;
17 | import me.blvckbytes.quick_shop_search.command.ReloadCommand;
18 | import me.blvckbytes.quick_shop_search.command.SubCommand_Advertise;
19 | import me.blvckbytes.quick_shop_search.config.*;
20 | import me.blvckbytes.quick_shop_search.config.commands.QuickShopSearchCommandSection;
21 | import me.blvckbytes.quick_shop_search.config.commands.QuickShopSearchLanguageCommandSection;
22 | import me.blvckbytes.quick_shop_search.config.commands.QuickShopSearchReloadCommandSection;
23 | import me.blvckbytes.quick_shop_search.display.result.ResultDisplayHandler;
24 | import me.blvckbytes.quick_shop_search.display.result.SelectionStateStore;
25 | import me.blvckbytes.quick_shop_search.display.teleport.TeleportDisplayHandler;
26 | import me.blvckbytes.quick_shop_search.integration.IntegrationRegistry;
27 | import net.kyori.adventure.text.Component;
28 | import org.bukkit.Bukkit;
29 | import org.bukkit.Material;
30 | import org.bukkit.plugin.java.JavaPlugin;
31 |
32 | import java.util.Objects;
33 | import java.util.logging.Level;
34 |
35 | public class QuickShopSearchPlugin extends JavaPlugin {
36 |
37 | private ResultDisplayHandler resultDisplayHandler;
38 | private TeleportDisplayHandler teleportDisplayHandler;
39 | private SelectionStateStore stateStore;
40 | private UidScopedNamedStampStore stampStore;
41 |
42 | @Override
43 | public void onEnable() {
44 | var logger = getLogger();
45 |
46 | try {
47 | // First invocation is quite heavy - warm up cache
48 | XMaterial.matchXMaterial(Material.AIR);
49 |
50 | var foliaLib = new FoliaLib(this);
51 | var scheduler = foliaLib.getScheduler();
52 |
53 | var offlinePlayerRegistry = new OfflinePlayerRegistry();
54 | Bukkit.getPluginManager().registerEvents(offlinePlayerRegistry, this);
55 |
56 | var texturesResolverFunction = new Base64TexturesResolverFunction(logger, offlinePlayerRegistry);
57 |
58 | var configManager = new ConfigManager(this, "config", texturesResolverFunction::registerSelf);
59 | var config = new ConfigKeeper<>(configManager, "config.yml", MainSection.class);
60 |
61 | var parserPlugin = ItemPredicateParserPlugin.getInstance();
62 |
63 | if (parserPlugin == null)
64 | throw new IllegalStateException("Depending on ItemPredicateParser to be successfully loaded");
65 |
66 | logger.info("Using language " + config.rootSection.predicates.mainLanguage.assetFileNameWithoutExtension + " for predicate parsing");
67 |
68 | var chatPromptManager = new ChatPromptManager(scheduler);
69 | Bukkit.getServer().getPluginManager().registerEvents(chatPromptManager, this);
70 |
71 | var versionDependentFactory = new QuickShopVersionDependentFactory(logger);
72 | var remoteInteractionApi = versionDependentFactory.createInteractionApi();
73 |
74 | stateStore = new SelectionStateStore(this, logger);
75 | stampStore = new UidScopedNamedStampStore(this, logger);
76 |
77 | var slowTeleportManager = new SlowTeleportManager(scheduler, config);
78 | Bukkit.getServer().getPluginManager().registerEvents(slowTeleportManager, this);
79 |
80 | teleportDisplayHandler = new TeleportDisplayHandler(config, scheduler, slowTeleportManager);
81 | Bukkit.getPluginManager().registerEvents(teleportDisplayHandler, this);
82 |
83 | var integrationRegistry = new IntegrationRegistry(config, logger, scheduler, this);
84 |
85 | resultDisplayHandler = new ResultDisplayHandler(
86 | logger,
87 | scheduler,
88 | remoteInteractionApi,
89 | config,
90 | stateStore,
91 | stampStore,
92 | chatPromptManager,
93 | teleportDisplayHandler,
94 | integrationRegistry
95 | );
96 |
97 | var shopRegistry = new CachedShopRegistry(this, scheduler, resultDisplayHandler, config, integrationRegistry, logger);
98 | var shopEventHandler = versionDependentFactory.createListener(shopRegistry);
99 |
100 | Bukkit.getPluginManager().registerEvents(shopRegistry, this);
101 | Bukkit.getPluginManager().registerEvents(shopEventHandler, this);
102 | Bukkit.getPluginManager().registerEvents(resultDisplayHandler, this);
103 |
104 | var commandUpdater = new CommandUpdater(this);
105 | var commandExecutor = new QuickShopSearchCommand(scheduler, parserPlugin.getPredicateHelper(), shopRegistry, config, resultDisplayHandler, offlinePlayerRegistry);
106 |
107 | var mainCommand = Objects.requireNonNull(getCommand(QuickShopSearchCommandSection.INITIAL_NAME));
108 | var languageCommand = Objects.requireNonNull(getCommand(QuickShopSearchLanguageCommandSection.INITIAL_NAME));
109 | var reloadCommand = Objects.requireNonNull(getCommand(QuickShopSearchReloadCommandSection.INITIAL_NAME));
110 |
111 | mainCommand.setExecutor(commandExecutor);
112 | languageCommand.setExecutor(commandExecutor);
113 | reloadCommand.setExecutor(new ReloadCommand(logger, config));
114 |
115 | Runnable updateCommands = () -> {
116 | config.rootSection.commands.quickShopSearch.apply(mainCommand, commandUpdater);
117 | config.rootSection.commands.quickShopSearchLanguage.apply(languageCommand, commandUpdater);
118 | config.rootSection.commands.quickShopSearchReload.apply(reloadCommand, commandUpdater);
119 |
120 | commandUpdater.trySyncCommands();
121 | };
122 |
123 | updateCommands.run();
124 | config.registerReloadListener(updateCommands);
125 |
126 | Bukkit.getPluginManager().registerEvents(new CommandSendListener(this, config), this);
127 |
128 | var advertiseCommandContainer = CommandContainer.builder()
129 | .prefix("advertise")
130 | .permission(PluginPermission.ADVERTISE_COMMAND.node)
131 | .executor(new SubCommand_Advertise(shopRegistry, config))
132 | .build();
133 |
134 | QuickShop.getInstance().getCommandManager().registerCmd(advertiseCommandContainer);
135 |
136 | // Set description afterward, because at the time of writing this, the current version of
137 | // QuickShop-Hikari annihilates the description when registering the container.
138 | advertiseCommandContainer.setDescription(locale -> {
139 | BukkitEvaluable description;
140 |
141 | if ((description = config.rootSection.playerMessages.commandAdvertiseDescription) != null)
142 | return Component.text(description.asScalar(ScalarType.STRING, config.rootSection.builtBaseEnvironment));
143 |
144 | return Component.text("Missing corresponding config-key");
145 | });
146 | } catch (Exception e) {
147 | logger.log(Level.SEVERE, "Could not initialize plugin", e);
148 | Bukkit.getPluginManager().disablePlugin(this);
149 | }
150 | }
151 |
152 | @Override
153 | public void onDisable() {
154 | if (resultDisplayHandler != null)
155 | resultDisplayHandler.onShutdown();
156 |
157 | if (teleportDisplayHandler != null)
158 | teleportDisplayHandler.onShutdown();
159 |
160 | if (stateStore != null)
161 | stateStore.onShutdown();
162 |
163 | if (stampStore != null)
164 | stampStore.onShutdown();
165 | }
166 | }
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/UidScopedNamedStampStore.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search;
2 |
3 | import com.google.common.base.Charsets;
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 | import com.google.gson.JsonObject;
7 | import com.google.gson.JsonPrimitive;
8 | import com.google.gson.stream.JsonWriter;
9 | import org.bukkit.Bukkit;
10 | import org.bukkit.plugin.Plugin;
11 |
12 | import java.io.File;
13 | import java.io.FileReader;
14 | import java.io.FileWriter;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.UUID;
18 | import java.util.logging.Level;
19 | import java.util.logging.Logger;
20 |
21 | public class UidScopedNamedStampStore {
22 |
23 | private static final Gson GSON_INSTANCE = new GsonBuilder().setPrettyPrinting().create();
24 |
25 | private final File dataFile;
26 | private final Logger logger;
27 |
28 | private final Map> namedStampsByUid;
29 | private boolean isDataDirty;
30 |
31 | public UidScopedNamedStampStore(Plugin plugin, Logger logger) {
32 | this.dataFile = new File(plugin.getDataFolder(), "cooldown_persistence.json");
33 | this.logger = logger;
34 | this.namedStampsByUid = new HashMap<>();
35 |
36 | loadFromDisk();
37 |
38 | Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, this::saveToDisk, 0L, 20L * 5);
39 | }
40 |
41 | public void write(UUID id, String name, long value) {
42 | isDataDirty = true;
43 |
44 | namedStampsByUid
45 | .computeIfAbsent(id, k -> new HashMap<>())
46 | .put(name.toLowerCase(), value);
47 | }
48 |
49 | /**
50 | * @return -1 if non-existent; value >= 0 otherwise
51 | */
52 | public long read(UUID id, String name) {
53 | var stampByName = namedStampsByUid.get(id);
54 |
55 | if (stampByName == null)
56 | return -1;
57 |
58 | return stampByName.getOrDefault(name.toLowerCase(), -1L);
59 | }
60 |
61 | public void onShutdown() {
62 | saveToDisk();
63 | }
64 |
65 | private void loadFromDisk() {
66 | if (!dataFile.isFile())
67 | return;
68 |
69 | try (
70 | var reader = new FileReader(dataFile, Charsets.UTF_8);
71 | ) {
72 | var jsonData = GSON_INSTANCE.fromJson(reader, JsonObject.class);
73 |
74 | if (jsonData == null)
75 | return;
76 |
77 | for (var jsonEntry : jsonData.entrySet()) {
78 | var idString = jsonEntry.getKey();
79 |
80 | UUID id;
81 |
82 | try {
83 | id = UUID.fromString(idString);
84 | } catch (Exception e) {
85 | logger.log(Level.WARNING, "Could not parse ID \"" + idString + "\" of state-file at " + dataFile, e);
86 | continue;
87 | }
88 |
89 | if (!(jsonEntry.getValue() instanceof JsonObject stampByNameObject)) {
90 | logger.warning("Value at ID \"" + idString + "\" of state-file at " + dataFile + " was not a map");
91 | continue;
92 | }
93 |
94 | var stampByName = new HashMap();
95 |
96 | for (var stampEntry : stampByNameObject.entrySet()) {
97 | var name = stampEntry.getKey();
98 |
99 | if (!(stampEntry.getValue() instanceof JsonPrimitive valuePrimitive)) {
100 | logger.warning("Value at ID \"" + idString + "\" and name \"" + name + "\" of state-file at " + dataFile + " was not a primitive");
101 | continue;
102 | }
103 |
104 | long value;
105 |
106 | try {
107 | value = valuePrimitive.getAsLong();
108 | } catch (Exception e) {
109 | logger.warning("Value at ID \"" + idString + "\" and name \"" + name + "\" of state-file at " + dataFile + " was malformed");
110 | continue;
111 | }
112 |
113 | stampByName.put(name, value);
114 | }
115 |
116 | namedStampsByUid.put(id, stampByName);
117 | }
118 | } catch (Exception e) {
119 | logger.log(Level.SEVERE, "Could not read state-file at " + dataFile, e);
120 | }
121 | }
122 |
123 | private void saveToDisk() {
124 | if (!isDataDirty)
125 | return;
126 |
127 | isDataDirty = false;
128 |
129 | try (
130 | var fileWriter = new FileWriter(dataFile, Charsets.UTF_8);
131 | var jsonWriter = new JsonWriter(fileWriter);
132 | ) {
133 | GSON_INSTANCE.toJson(this.namedStampsByUid, Map.class, jsonWriter);
134 | } catch (Exception e) {
135 | logger.log(Level.SEVERE, "Could not write state-file to " + dataFile, e);
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/cache/QuickShopVersionDependentFactory.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.cache;
2 |
3 | import me.blvckbytes.quick_shop_search.compatibility.*;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.event.Listener;
6 | import org.bukkit.plugin.java.JavaPlugin;
7 |
8 | import java.util.logging.Logger;
9 |
10 | public class QuickShopVersionDependentFactory {
11 |
12 | private static final String PLUGIN_NAME = "QuickShop-Hikari";
13 | private static final int[]
14 | VER_6207 = new int[] { 6, 2, 0, 7 },
15 | VER_6208 = new int[] { 6, 2, 0, 8 }
16 | ;
17 |
18 | private final Logger logger;
19 | private final int[] quickShopVersion;
20 |
21 | public QuickShopVersionDependentFactory(Logger logger) {
22 | this.logger = logger;
23 |
24 | var quickShopReference = Bukkit.getPluginManager().getPlugin(PLUGIN_NAME);
25 |
26 | if (!(quickShopReference instanceof JavaPlugin quickShopPlugin))
27 | throw new IllegalStateException("Could not get a reference to " + PLUGIN_NAME);
28 |
29 | var quickShopVersionString = quickShopPlugin.getDescription().getVersion();
30 | quickShopVersion = parseVersionString(quickShopVersionString);
31 |
32 | logger.info("Detected " + PLUGIN_NAME + " version " + quickShopVersionString);
33 |
34 | if (quickShopVersion.length != 4)
35 | throw new IllegalStateException("Expected " + PLUGIN_NAME + "'s version to be comprised of four parts");
36 | }
37 |
38 | public Listener createListener(QuickShopEventConsumer consumer) {
39 | if (compareVersions(quickShopVersion, VER_6207) <= 0) {
40 | try {
41 | // On dev-builds, they like to make breaking changes, but not bump the version-string, :)
42 | // Thus, ensure that it's actually a version prior to 6.2.0.7, by trying to access an event-class
43 | Class.forName("com.ghostchu.quickshop.api.event.ShopInventoryCalculateEvent");
44 |
45 | logger.info("Loaded listener-support for <= " + PLUGIN_NAME + " 6.2.0.7");
46 | return new QuickShopListener_LTE_6207(consumer);
47 | }
48 |
49 | // Fall through to loading the version which made the breaking changes
50 | catch (ClassNotFoundException ignored) {
51 | logger.info("Detected dev-version of 6.2.0.8, which introduced breaking-changes to events");
52 | }
53 | }
54 |
55 | if (compareVersions(quickShopVersion, VER_6208) <= 0) {
56 | logger.info("Loaded listener-support for <= " + PLUGIN_NAME + " 6.2.0.8 and > 6.2.0.7");
57 | return new QuickShopListener_GT_6207_LTE_6208(consumer);
58 | }
59 |
60 | logger.info("Loaded listener-support for > " + PLUGIN_NAME + " 6.2.0.8");
61 | return new QuickShopListener_GT_6208(consumer);
62 | }
63 |
64 | public RemoteInteractionApi createInteractionApi() {
65 | if (compareVersions(quickShopVersion, VER_6207) <= 0) {
66 | try {
67 | var result = new RemoteInteractionApi_LTE_6207();
68 | logger.info("Loaded remote-interaction-support for <= " + PLUGIN_NAME + " 6.2.0.7");
69 | return result;
70 | }
71 |
72 | catch (Exception ignored) {
73 | logger.info("Detected dev-version of 6.2.0.8, which introduced breaking-changes to remote-interaction");
74 | }
75 | }
76 |
77 | if (compareVersions(quickShopVersion, VER_6208) <= 0) {
78 | logger.info("Loaded remote-interaction-support for <= " + PLUGIN_NAME + " 6.2.0.8 and > 6.2.0.7");
79 | return new RemoteInteractionApi_GT_6207_LTE_6208();
80 | }
81 |
82 | logger.info("Loaded remote-interaction-support for > " + PLUGIN_NAME + " 6.2.0.8");
83 | return new RemoteInteractionApi_GT_6208();
84 | }
85 |
86 | private static int compareVersions(int[] a, int[] b) {
87 | if (a.length != b.length)
88 | throw new IllegalStateException("Tried to compare versions of different lengths: " + a.length + " and " + b.length);
89 |
90 | for (var i = 0; i < a.length; ++i) {
91 | var aPart = a[i];
92 | var bPart = b[i];
93 |
94 | if (aPart == bPart)
95 | continue;
96 |
97 | return Integer.compare(aPart, bPart);
98 | }
99 |
100 | return 0;
101 | }
102 |
103 | private static int[] parseVersionString(String versionString) {
104 | var versionParts = versionString.split("\\.");
105 | var versionNumbers = new int[versionParts.length];
106 |
107 | for (var i = 0; i < versionParts.length; ++i) {
108 | try {
109 | var versionPart = versionParts[i];
110 |
111 | // They've decided to append additional information with a dash to the last part
112 | // Example: "6.2.0.9-RELEASE-1"
113 | if (i == versionParts.length - 1) {
114 | var dashIndex = versionPart.indexOf('-');
115 |
116 | if (dashIndex > 0)
117 | versionPart = versionPart.substring(0, dashIndex);
118 | }
119 |
120 | versionNumbers[i] = Integer.parseInt(versionPart);
121 | } catch (NumberFormatException e) {
122 | throw new IllegalStateException("Could not parse " + (i + 1) + "-th version-part of version " + versionString, e);
123 | }
124 | }
125 |
126 | return versionNumbers;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/cache/ShopScalarDiff.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.cache;
2 |
3 | import com.ghostchu.quickshop.api.shop.ShopType;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.util.Objects;
7 |
8 | public class ShopScalarDiff {
9 |
10 | private final CachedShop shop;
11 |
12 | private int lastCachedSpace;
13 | private int lastCachedStock;
14 | private boolean lastIsUnlimited;
15 | private @Nullable String lastShopName;
16 | private double lastPrice;
17 | private @Nullable ShopType lastShopType;
18 |
19 | public ShopScalarDiff(CachedShop shop) {
20 | this.shop = shop;
21 | }
22 |
23 | public boolean update() {
24 | var unlimited = shop.handle.isUnlimited();
25 |
26 | boolean hadDelta;
27 |
28 | hadDelta = lastIsUnlimited != unlimited;
29 | lastIsUnlimited = unlimited;
30 |
31 | hadDelta |= !Objects.equals(lastShopName, shop.cachedName);
32 | lastShopName = shop.cachedName;
33 |
34 | hadDelta |= lastPrice != shop.cachedPrice;
35 | lastPrice = shop.cachedPrice;
36 |
37 | hadDelta |= !Objects.equals(lastShopType, shop.cachedType);
38 | lastShopType = shop.cachedType;
39 |
40 | hadDelta |= lastCachedSpace != shop.cachedSpace;
41 | lastCachedSpace = shop.cachedSpace;
42 |
43 | hadDelta |= lastCachedStock != shop.cachedStock;
44 | lastCachedStock = shop.cachedStock;
45 |
46 | return hadDelta;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/cache/ShopUpdate.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.cache;
2 |
3 | public enum ShopUpdate {
4 | CREATED,
5 | REMOVED,
6 | ITEM_CHANGED,
7 | PROPERTIES_CHANGED
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/cache/ToggleResult.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.cache;
2 |
3 | public enum ToggleResult {
4 | NOW_ON,
5 | NOW_OFF,
6 | NOT_ALLOWED,
7 | ERROR
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/command/CommandSendListener.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.command;
2 |
3 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
4 | import me.blvckbytes.quick_shop_search.PluginPermission;
5 | import me.blvckbytes.quick_shop_search.config.MainSection;
6 | import org.bukkit.command.Command;
7 | import org.bukkit.event.EventHandler;
8 | import org.bukkit.event.Listener;
9 | import org.bukkit.event.player.PlayerCommandSendEvent;
10 | import org.bukkit.plugin.java.JavaPlugin;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | public class CommandSendListener implements Listener {
16 |
17 | private final Map pluginCommands;
18 | private final String lowerPluginName;
19 |
20 | public CommandSendListener(JavaPlugin plugin, ConfigKeeper config) {
21 | this.pluginCommands = new HashMap<>();
22 | this.lowerPluginName = plugin.getName().toLowerCase();
23 |
24 | pluginCommands.put(
25 | plugin.getCommand(config.rootSection.commands.quickShopSearch.evaluatedName),
26 | PluginPermission.MAIN_COMMAND
27 | );
28 |
29 | pluginCommands.put(
30 | plugin.getCommand(config.rootSection.commands.quickShopSearchLanguage.evaluatedName),
31 | PluginPermission.LANGUAGE_COMMAND
32 | );
33 |
34 | pluginCommands.put(
35 | plugin.getCommand(config.rootSection.commands.quickShopSearchReload.evaluatedName),
36 | PluginPermission.RELOAD_COMMAND
37 | );
38 | }
39 |
40 | @EventHandler
41 | public void onCommandSend(PlayerCommandSendEvent event) {
42 | var player = event.getPlayer();
43 |
44 | for (var pluginCommandEntry : pluginCommands.entrySet()) {
45 | if (pluginCommandEntry.getValue().has(player))
46 | continue;
47 |
48 | var pluginCommand = pluginCommandEntry.getKey();
49 |
50 | event.getCommands().remove(pluginCommand.getName());
51 | event.getCommands().remove(lowerPluginName + ":" + pluginCommand.getName());
52 |
53 | for (var alias : pluginCommand.getAliases()) {
54 | event.getCommands().remove(alias);
55 | event.getCommands().remove(lowerPluginName + ":" + alias);
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/command/ReloadCommand.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.command;
2 |
3 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
4 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
5 | import me.blvckbytes.quick_shop_search.PluginPermission;
6 | import me.blvckbytes.quick_shop_search.config.MainSection;
7 | import org.bukkit.command.Command;
8 | import org.bukkit.command.CommandExecutor;
9 | import org.bukkit.command.CommandSender;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import java.util.logging.Level;
13 | import java.util.logging.Logger;
14 |
15 | public class ReloadCommand implements CommandExecutor {
16 |
17 | private final Logger logger;
18 | private final ConfigKeeper config;
19 |
20 | public ReloadCommand(Logger logger, ConfigKeeper config) {
21 | this.logger = logger;
22 | this.config = config;
23 | }
24 |
25 | @Override
26 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
27 | BukkitEvaluable message;
28 |
29 | if (!PluginPermission.RELOAD_COMMAND.has(sender)) {
30 | if ((message = config.rootSection.playerMessages.missingPermissionReloadCommand) != null)
31 | message.sendMessage(sender, config.rootSection.builtBaseEnvironment);
32 |
33 | return true;
34 | }
35 |
36 | try {
37 | this.config.reload();
38 |
39 | if ((message = config.rootSection.playerMessages.pluginReloadedSuccess) != null)
40 | message.sendMessage(sender, config.rootSection.builtBaseEnvironment);
41 | } catch (Exception e) {
42 | logger.log(Level.SEVERE, "An error occurred while trying to reload the config", e);
43 |
44 | if ((message = config.rootSection.playerMessages.pluginReloadedError) != null)
45 | message.sendMessage(sender, config.rootSection.builtBaseEnvironment);
46 | }
47 |
48 | return true;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/command/SearchFlag.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.command;
2 |
3 | import me.blvckbytes.quick_shop_search.OfflinePlayerRegistry;
4 | import me.blvckbytes.quick_shop_search.PluginPermission;
5 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
6 | import me.blvckbytes.syllables_matcher.EnumMatcher;
7 | import me.blvckbytes.syllables_matcher.EnumPredicate;
8 | import me.blvckbytes.syllables_matcher.MatchableEnum;
9 | import org.apache.commons.lang.StringUtils;
10 | import org.bukkit.OfflinePlayer;
11 | import org.bukkit.entity.Player;
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | import java.util.*;
16 |
17 | public abstract class SearchFlag implements MatchableEnum {
18 |
19 | private static final Map> searchFlagByName = new HashMap<>();
20 |
21 | public static final SearchFlag OWNER = new SearchFlag<>("OWNER", PluginPermission.FEATURE_SEARCH_FLAG_OWNER) {
22 |
23 | @Override
24 | public @Nullable OfflinePlayer parse(String value, OfflinePlayerRegistry offlinePlayerRegistry) {
25 | return offlinePlayerRegistry.getByName(value);
26 | }
27 |
28 | @Override
29 | public boolean _test(CachedShop shop, Player executor, @NotNull OfflinePlayer value) {
30 | var shopOwnerId = shop.handle.getOwner().getUniqueId();
31 | return shopOwnerId != null && shopOwnerId.equals(value.getUniqueId());
32 | }
33 | };
34 |
35 | public static final SearchFlag RADIUS = new SearchFlag<>("RADIUS", PluginPermission.FEATURE_SEARCH_FLAG_RADIUS) {
36 |
37 | @Override
38 | public @Nullable Integer parse(String value, OfflinePlayerRegistry offlinePlayerRegistry) {
39 | return SearchFlag.tryParsePositiveInteger(value);
40 | }
41 |
42 | @Override
43 | public boolean _test(CachedShop shop, Player executor, @NotNull Integer value) {
44 | var shopLocation = shop.handle.getLocation();
45 | var playerLocation = executor.getLocation();
46 | var shopWorld = shopLocation.getWorld();
47 |
48 | if (shopWorld == null || !shopWorld.equals(executor.getWorld()))
49 | return false;
50 |
51 | return shopLocation.distanceSquared(playerLocation) < value * value;
52 | }
53 | };
54 |
55 | public static final SearchFlag PRICE = new SearchFlag<>("PRICE", PluginPermission.FEATURE_SEARCH_FLAG_PRICE) {
56 |
57 | @Override
58 | public @Nullable Double parse(String value, OfflinePlayerRegistry offlinePlayerRegistry) {
59 | return SearchFlag.tryParsePositiveDouble(value);
60 | }
61 |
62 | @Override
63 | public boolean _test(CachedShop shop, Player executor, @NotNull Double value) {
64 | return Math.abs(shop.handle.getPrice() - value) < .001;
65 | }
66 | };
67 |
68 | public static final SearchFlag MAX_PRICE = new SearchFlag<>("MAX_PRICE", PluginPermission.FEATURE_SEARCH_FLAG_MAX_PRICE) {
69 |
70 | @Override
71 | public @Nullable Double parse(String value, OfflinePlayerRegistry offlinePlayerRegistry) {
72 | return SearchFlag.tryParsePositiveDouble(value);
73 | }
74 |
75 | @Override
76 | public boolean _test(CachedShop shop, Player executor, @NotNull Double value) {
77 | return shop.handle.getPrice() <= value;
78 | }
79 | };
80 |
81 | public static final SearchFlag MIN_PRICE = new SearchFlag<>("MIN_PRICE", PluginPermission.FEATURE_SEARCH_FLAG_MIN_PRICE) {
82 |
83 | @Override
84 | public @Nullable Double parse(String value, OfflinePlayerRegistry offlinePlayerRegistry) {
85 | return SearchFlag.tryParsePositiveDouble(value);
86 | }
87 |
88 | @Override
89 | public boolean _test(CachedShop shop, Player executor, @NotNull Double value) {
90 | return shop.handle.getPrice() >= value;
91 | }
92 | };
93 |
94 | public static final EnumMatcher> matcher = new EnumMatcher<>(searchFlagByName.values());
95 |
96 | private final String name;
97 | private final PluginPermission permission;
98 | private @Nullable List suggestions;
99 |
100 | private SearchFlag(String name, PluginPermission permission) {
101 | this.name = name;
102 | this.permission = permission;
103 |
104 | searchFlagByName.put(name.toLowerCase(), this);
105 | }
106 |
107 | public abstract @Nullable T parse(String value, OfflinePlayerRegistry offlinePlayerRegistry);
108 |
109 | protected abstract boolean _test(CachedShop shop, Player executor, @NotNull T value);
110 |
111 | @SuppressWarnings("unchecked")
112 | public boolean test(CachedShop shop, Player executor, @NotNull Object value) {
113 | return _test(shop, executor, (T) value);
114 | }
115 |
116 | public void setSuggestions(@Nullable List suggestions) {
117 | this.suggestions = suggestions;
118 | }
119 |
120 | public List getSuggestions(String input) {
121 | if (this.suggestions != null) {
122 | return this.suggestions.stream()
123 | .filter(it -> StringUtils.startsWithIgnoreCase(it, input))
124 | .toList();
125 | }
126 |
127 | return List.of("[value]");
128 | }
129 |
130 | @Override
131 | public String name() {
132 | return name;
133 | }
134 |
135 | @Override
136 | public int hashCode() {
137 | return name.hashCode();
138 | }
139 |
140 | @Override
141 | public boolean equals(Object o) {
142 | if (this == o) return true;
143 | if (!(o instanceof SearchFlag> that)) return false;
144 | return Objects.equals(name, that.name);
145 | }
146 |
147 | public static @Nullable SearchFlag> getByName(String name) {
148 | return searchFlagByName.get(name.toLowerCase());
149 | }
150 |
151 | public static EnumPredicate> permissionPredicate(Player player) {
152 | return flag -> flag.constant.permission.has(player);
153 | }
154 |
155 | private static @Nullable Integer tryParsePositiveInteger(String value) {
156 | try {
157 | var result = Integer.parseInt(value);
158 |
159 | if (result < 0)
160 | return null;
161 |
162 | return result;
163 | } catch (Exception e) {
164 | return null;
165 | }
166 | }
167 |
168 | private static @Nullable Double tryParsePositiveDouble(String value) {
169 | try {
170 | var result = Double.parseDouble(value);
171 |
172 | if (result < 0)
173 | return null;
174 |
175 | return result;
176 | } catch (Exception e) {
177 | return null;
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/command/SearchFlagsContainer.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.command;
2 |
3 | import me.blvckbytes.quick_shop_search.OfflinePlayerRegistry;
4 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
5 | import org.bukkit.entity.Player;
6 |
7 | import javax.annotation.Nullable;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 | import java.util.function.Function;
11 |
12 | public class SearchFlagsContainer {
13 |
14 | private final Map, Object> valueByFlag;
15 |
16 | public SearchFlagsContainer() {
17 | this.valueByFlag = new HashMap<>();
18 | }
19 |
20 | public int size() {
21 | return valueByFlag.size();
22 | }
23 |
24 | @SuppressWarnings("unchecked")
25 | public @Nullable T get(SearchFlag flag) {
26 | var value = valueByFlag.get(flag);
27 |
28 | if (value == null)
29 | return null;
30 |
31 | return (T) value;
32 | }
33 |
34 | @SuppressWarnings("unchecked")
35 | public @Nullable R getWithMapper(SearchFlag flag, Function mapper) {
36 | var value = valueByFlag.get(flag);
37 |
38 | if (value == null)
39 | return null;
40 |
41 | return mapper.apply((T) value);
42 | }
43 |
44 | public boolean parseAndSet(SearchFlag> flag, String value, OfflinePlayerRegistry offlinePlayerRegistry) {
45 | var parsedValue = flag.parse(value, offlinePlayerRegistry);
46 |
47 | if (parsedValue == null)
48 | return false;
49 |
50 | this.valueByFlag.put(flag, parsedValue);
51 | return true;
52 | }
53 |
54 | public boolean test(CachedShop shop, Player executor) {
55 | for (var valueEntry : valueByFlag.entrySet()) {
56 | if (!valueEntry.getKey().test(shop, executor, valueEntry.getValue()))
57 | return false;
58 | }
59 |
60 | return true;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/command/SubCommand_Advertise.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.command;
2 |
3 | import com.ghostchu.quickshop.api.command.CommandHandler;
4 | import com.ghostchu.quickshop.api.command.CommandParser;
5 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
6 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
7 | import me.blvckbytes.quick_shop_search.PluginPermission;
8 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
9 | import me.blvckbytes.quick_shop_search.cache.CachedShopRegistry;
10 | import me.blvckbytes.quick_shop_search.config.MainSection;
11 | import org.bukkit.entity.Player;
12 | import org.bukkit.util.BlockIterator;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.util.List;
17 |
18 | public class SubCommand_Advertise implements CommandHandler {
19 |
20 | private final CachedShopRegistry shopRegistry;
21 | private final ConfigKeeper config;
22 |
23 | public SubCommand_Advertise(CachedShopRegistry shopRegistry, ConfigKeeper config) {
24 | this.shopRegistry = shopRegistry;
25 | this.config = config;
26 | }
27 |
28 | @Override
29 | public void onCommand(Player player, @NotNull String commandLabel, @NotNull CommandParser parser) {
30 | BukkitEvaluable message;
31 |
32 | if (!PluginPermission.ADVERTISE_COMMAND.has(player)) {
33 | if ((message = config.rootSection.playerMessages.missingPermissionAdvertiseCommand) != null)
34 | message.sendMessage(player, config.rootSection.builtBaseEnvironment);
35 |
36 | return;
37 | }
38 |
39 | var targetShop = getLookedAtShop(player);
40 |
41 | if (targetShop == null) {
42 | if ((message = config.rootSection.playerMessages.commandAdvertiseNotLookingAtShop) != null)
43 | message.sendMessage(player, config.rootSection.builtBaseEnvironment);
44 |
45 | return;
46 | }
47 |
48 | var shopOwner = targetShop.handle.getOwner().getBukkitPlayer().orElse(null);
49 | var isShopOwner = shopOwner != null && shopOwner == player;
50 |
51 | if (!PluginPermission.ADVERTISE_COMMAND_OWNER_BYPASS.has(player) && !isShopOwner) {
52 | if ((message = config.rootSection.playerMessages.commandAdvertiseNotTheOwner) != null)
53 | message.sendMessage(player, config.rootSection.builtBaseEnvironment);
54 |
55 | return;
56 | }
57 |
58 | var toggleResult = targetShop.toggleAdvertising(player);
59 |
60 | switch (toggleResult) {
61 | case NOW_ON: {
62 | if (isShopOwner)
63 | message = config.rootSection.playerMessages.commandAdvertiseEnabledSelf;
64 | else
65 | message = config.rootSection.playerMessages.commandAdvertiseEnabledOther;
66 | break;
67 | }
68 |
69 | case NOW_OFF: {
70 | if (isShopOwner)
71 | message = config.rootSection.playerMessages.commandAdvertiseDisabledSelf;
72 | else
73 | message = config.rootSection.playerMessages.commandAdvertiseDisabledOther;
74 | break;
75 | }
76 |
77 | case NOT_ALLOWED: {
78 | if (isShopOwner)
79 | message = config.rootSection.playerMessages.commandAdvertiseDisallowedSelf;
80 | else
81 | message = config.rootSection.playerMessages.commandAdvertiseDisallowedOther;
82 | break;
83 | }
84 |
85 | default: {
86 | message = config.rootSection.playerMessages.commandAdvertiseToggleError;
87 | break;
88 | }
89 | }
90 |
91 | if (message != null)
92 | message.sendMessage(player, targetShop.getShopEnvironment().build());
93 | }
94 |
95 | @Override
96 | public @Nullable List onTabComplete(@NotNull Player sender, @NotNull String commandLabel, @NotNull CommandParser parser) {
97 | return List.of();
98 | }
99 |
100 | private @Nullable CachedShop getLookedAtShop(Player player) {
101 | var blockIterator = new BlockIterator(player, 10);
102 |
103 | while(blockIterator.hasNext()) {
104 | var block = blockIterator.next();
105 | var shop = shopRegistry.findByLocation(block.getLocation());
106 |
107 | if(shop == null)
108 | continue;
109 |
110 | return shop;
111 | }
112 |
113 | return null;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/config/Base64TexturesResolverFunction.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.config;
2 |
3 | import com.google.gson.*;
4 | import me.blvckbytes.gpeee.functions.AExpressionFunction;
5 | import me.blvckbytes.gpeee.functions.ExpressionFunctionArgument;
6 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
7 | import me.blvckbytes.gpeee.interpreter.IEvaluationEnvironment;
8 | import me.blvckbytes.quick_shop_search.OfflinePlayerRegistry;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import java.net.URI;
12 | import java.net.http.HttpClient;
13 | import java.net.http.HttpRequest;
14 | import java.net.http.HttpResponse;
15 | import java.util.HashMap;
16 | import java.util.List;
17 | import java.util.Map;
18 | import java.util.UUID;
19 | import java.util.logging.Level;
20 | import java.util.logging.Logger;
21 |
22 | public class Base64TexturesResolverFunction extends AExpressionFunction {
23 |
24 | private final Logger logger;
25 | private final OfflinePlayerRegistry offlinePlayerRegistry;
26 | private final HttpClient httpClient;
27 | private final Gson gson;
28 | private final Map texturesByOwnerId;
29 |
30 | public Base64TexturesResolverFunction(Logger logger, OfflinePlayerRegistry offlinePlayerRegistry) {
31 | this.logger = logger;
32 | this.offlinePlayerRegistry = offlinePlayerRegistry;
33 | this.httpClient = HttpClient.newHttpClient();
34 | this.gson = new GsonBuilder().create();
35 | this.texturesByOwnerId = new HashMap<>();
36 | }
37 |
38 | @Override
39 | public Object apply(IEvaluationEnvironment iEvaluationEnvironment, List<@Nullable Object> args) {
40 | String ownerName = nullable(args, 0);
41 |
42 | if (ownerName == null)
43 | return null;
44 |
45 | var ownerPlayer = offlinePlayerRegistry.getByName(ownerName);
46 |
47 | if (ownerPlayer == null)
48 | return null;
49 |
50 | return texturesByOwnerId.computeIfAbsent(ownerPlayer.getUniqueId(), this::tryResolveTextures);
51 | }
52 |
53 | private @Nullable String tryResolveTextures(UUID ownerId) {
54 | try {
55 | var ownerIdString = ownerId.toString().replace("-", "");
56 |
57 | var httpRequest = HttpRequest
58 | .newBuilder(URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + ownerIdString))
59 | .GET()
60 | .build();
61 |
62 | var response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
63 |
64 | if (response.statusCode() != 200)
65 | throw new IllegalStateException("Expected status-code 200, but got " + response.statusCode());
66 |
67 | var responseBody = response.body();
68 | var responseJson = gson.fromJson(responseBody, JsonObject.class);
69 |
70 | if (!(responseJson.get("properties") instanceof JsonArray propertiesArray))
71 | throw new IllegalStateException("Expected key \"properties\" to be of type array");
72 |
73 | String texturesValue = null;
74 |
75 | for (var property : propertiesArray) {
76 | if (!(property instanceof JsonObject propertyObject))
77 | throw new IllegalStateException("Expected item of \"properties\"-array to be an object");
78 |
79 | if (!(propertyObject.get("name") instanceof JsonPrimitive namePrimitive))
80 | throw new IllegalStateException("Expected \"name\" of property within \"properties\"-array to be a string");
81 |
82 | if (!"textures".equals(namePrimitive.getAsString()))
83 | continue;
84 |
85 | if (!(propertyObject.get("value") instanceof JsonPrimitive valuePrimitive))
86 | throw new IllegalStateException("Expected \"value\" of property within \"properties\"-array to be a string");
87 |
88 | texturesValue = valuePrimitive.getAsString();
89 | break;
90 | }
91 |
92 | if (texturesValue == null)
93 | throw new IllegalStateException("Could not locate \"textures\"-property within \"properties\"-array");
94 |
95 | return texturesValue;
96 | } catch (Throwable e) {
97 | logger.log(Level.SEVERE, "Could not dispatch a request to Mojang's profile-API in order to retrieve skin-textures", e);
98 | }
99 |
100 | return null;
101 | }
102 |
103 | @Override
104 | public @Nullable List getArguments() {
105 | return List.of(
106 | new ExpressionFunctionArgument("owner_name", "Name of the skull-owner", false, String.class)
107 | );
108 | }
109 |
110 | public void registerSelf(EvaluationEnvironmentBuilder environmentBuilder) {
111 | environmentBuilder.withFunction("resolve_b64_textures", this);
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/Display.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
5 | import me.blvckbytes.quick_shop_search.config.MainSection;
6 | import org.bukkit.entity.Player;
7 | import org.bukkit.inventory.Inventory;
8 |
9 | public abstract class Display {
10 |
11 | protected final Player player;
12 | protected final ConfigKeeper config;
13 | protected final PlatformScheduler scheduler;
14 | public final DisplayDataType displayData;
15 | protected Inventory inventory;
16 |
17 | protected Display(
18 | Player player,
19 | DisplayDataType displayData,
20 | ConfigKeeper config,
21 | PlatformScheduler scheduler
22 | ) {
23 | this.player = player;
24 | this.displayData = displayData;
25 | this.config = config;
26 | this.scheduler = scheduler;
27 | }
28 |
29 | public void show() {
30 | var priorInventory = inventory;
31 | inventory = makeInventory();
32 |
33 | renderItems();
34 |
35 | scheduler.runAtEntity(player, scheduleTask -> {
36 | // Make sure to open the newly rendered inventory first as to avoid flicker
37 | player.openInventory(inventory);
38 |
39 | // Avoid the case of the client not accepting opening the new inventory
40 | // and then being able to take items out of there. This way, we're safe.
41 | if (priorInventory != null)
42 | priorInventory.clear();
43 | });
44 | }
45 |
46 | protected abstract void renderItems();
47 |
48 | protected abstract Inventory makeInventory();
49 |
50 | public abstract void onConfigReload();
51 |
52 | public void onInventoryClose() {
53 | if (this.inventory != null)
54 | this.inventory.clear();
55 | }
56 |
57 | public void onShutdown() {
58 | if (inventory != null)
59 | inventory.clear();
60 |
61 | if (player.getOpenInventory().getTopInventory() == inventory)
62 | player.closeInventory();
63 | }
64 |
65 | public boolean isInventory(Inventory inventory) {
66 | return this.inventory == inventory;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/DisplayHandler.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
5 | import me.blvckbytes.quick_shop_search.config.MainSection;
6 | import org.bukkit.entity.Player;
7 | import org.bukkit.event.EventHandler;
8 | import org.bukkit.event.Listener;
9 | import org.bukkit.event.inventory.*;
10 | import org.bukkit.event.player.PlayerQuitEvent;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 | import java.util.UUID;
15 | import java.util.function.Consumer;
16 |
17 | public abstract class DisplayHandler, DisplayDataType> implements Listener {
18 |
19 | // If players move to their own inventory and close the UI quickly enough, the server will send back a packet
20 | // undoing that slot which assumed the top-inventory to still be open, and thus the undo won't work. For survival,
21 | // it's merely cosmetic, but for creative, the client will actually call this item into existence. While not
22 | // necessarily critical, it just makes the plugin look bad. On closing the inventory, if the last move happened
23 | // within a certain threshold of time, let's just update the player's inventory, as to make that ghost-item vanish.
24 | private static final long MOVE_GHOST_ITEM_THRESHOLD_MS = 500;
25 |
26 | protected final ConfigKeeper config;
27 | protected final PlatformScheduler scheduler;
28 |
29 | private final Map displayByPlayerId;
30 | private final Map lastMoveToOwnInventoryStampByPlayerId;
31 |
32 | protected DisplayHandler(
33 | ConfigKeeper config,
34 | PlatformScheduler scheduler
35 | ) {
36 | this.displayByPlayerId = new HashMap<>();
37 | this.lastMoveToOwnInventoryStampByPlayerId = new HashMap<>();
38 | this.config = config;
39 | this.scheduler = scheduler;
40 |
41 | config.registerReloadListener(() -> {
42 | for (var display : displayByPlayerId.values())
43 | display.onConfigReload();
44 | });
45 | }
46 |
47 | public abstract DisplayType instantiateDisplay(Player player, DisplayDataType displayData);
48 |
49 | public void show(Player player, DisplayDataType displayData) {
50 | displayByPlayerId.put(player.getUniqueId(), instantiateDisplay(player, displayData));
51 | }
52 |
53 | public void reopen(DisplayType display) {
54 | displayByPlayerId.put(display.player.getUniqueId(), display);
55 | display.show();
56 | }
57 |
58 | protected abstract void handleClick(Player player, DisplayType display, ClickType clickType, int slot);
59 |
60 | protected void forEachDisplay(Consumer consumer) {
61 | for (var display : displayByPlayerId.values())
62 | consumer.accept(display);
63 | }
64 |
65 | public void onShutdown() {
66 | for (var displayIterator = displayByPlayerId.entrySet().iterator(); displayIterator.hasNext();) {
67 | displayIterator.next().getValue().onShutdown();
68 | displayIterator.remove();
69 | }
70 | }
71 |
72 | @EventHandler
73 | public void onInventoryDrag(InventoryDragEvent event) {
74 | if (!(event.getWhoClicked() instanceof Player player))
75 | return;
76 |
77 | if (displayByPlayerId.containsKey(player.getUniqueId()))
78 | event.setCancelled(true);
79 | }
80 |
81 | @EventHandler
82 | public void onQuit(PlayerQuitEvent event) {
83 | var display = displayByPlayerId.remove(event.getPlayer().getUniqueId());
84 |
85 | if (display != null)
86 | display.onInventoryClose();
87 | }
88 |
89 | @EventHandler
90 | public void onInventoryClose(InventoryCloseEvent event) {
91 | if (!(event.getPlayer() instanceof Player player))
92 | return;
93 |
94 | var playerId = player.getUniqueId();
95 | var display = displayByPlayerId.get(playerId);
96 |
97 | // Only remove on inventory match, as to prevent removal on title update
98 | if (display != null && display.isInventory(event.getInventory())) {
99 | display.onInventoryClose();
100 | displayByPlayerId.remove(playerId);
101 |
102 | var lastMoveToOwnInventoryStamp = lastMoveToOwnInventoryStampByPlayerId.remove(playerId);
103 |
104 | if (
105 | lastMoveToOwnInventoryStamp != null &&
106 | System.currentTimeMillis() - lastMoveToOwnInventoryStamp < MOVE_GHOST_ITEM_THRESHOLD_MS
107 | ) {
108 | scheduler.runNextTick(task -> player.updateInventory());
109 | }
110 | }
111 | }
112 |
113 | @EventHandler
114 | public void onInventoryClick(InventoryClickEvent event) {
115 | if (!(event.getWhoClicked() instanceof Player player))
116 | return;
117 |
118 | var display = displayByPlayerId.get(player.getUniqueId());
119 |
120 | if (display == null)
121 | return;
122 |
123 | event.setCancelled(true);
124 |
125 | if (
126 | event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY &&
127 | event.getClickedInventory() != player.getInventory()
128 | ) {
129 | lastMoveToOwnInventoryStampByPlayerId.put(player.getUniqueId(), System.currentTimeMillis());
130 | }
131 |
132 | if (!display.isInventory(player.getOpenInventory().getTopInventory()))
133 | return;
134 |
135 | var slot = event.getRawSlot();
136 |
137 | if (slot < 0 || slot >= config.rootSection.resultDisplay.getRows() * 9)
138 | return;
139 |
140 | handleClick(player, display, event.getClick(), slot);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/AsyncTaskQueue.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class AsyncTaskQueue {
9 |
10 | private static class QueuedTask {
11 | Runnable runnable;
12 | boolean dispatched;
13 |
14 | public QueuedTask(Runnable runnable) {
15 | this.runnable = runnable;
16 | this.dispatched = false;
17 | }
18 | }
19 |
20 | private final PlatformScheduler scheduler;
21 | private final List taskQueue;
22 |
23 | public AsyncTaskQueue(PlatformScheduler scheduler) {
24 | this.scheduler = scheduler;
25 | this.taskQueue = new ArrayList<>();
26 | }
27 |
28 | public void enqueue(Runnable runnable) {
29 | synchronized (taskQueue) {
30 | taskQueue.add(new QueuedTask(runnable));
31 | processQueue();
32 | }
33 | }
34 |
35 | private void processQueue() {
36 | synchronized (taskQueue) {
37 | if (taskQueue.isEmpty())
38 | return;
39 |
40 | var task = taskQueue.get(0);
41 |
42 | // Task has already been dispatched and is still executing
43 | if (task.dispatched)
44 | return;
45 |
46 | task.dispatched = true;
47 |
48 | scheduler.runAsync(scheduleTask -> {
49 | task.runnable.run();
50 |
51 | synchronized (taskQueue) {
52 | taskQueue.remove(0);
53 | }
54 |
55 | processQueue();
56 | });
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/CalculatedFees.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import com.ghostchu.quickshop.api.shop.ShopType;
4 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
5 | import me.blvckbytes.quick_shop_search.config.fees.FeesValuesSection;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | public record CalculatedFees(
9 | double absoluteFees,
10 | double relativeFees,
11 | double relativeFeesValue
12 | ) {
13 | public static final CalculatedFees NO_FEES = new CalculatedFees(0, 0, 0);
14 |
15 | public boolean isNotZero() {
16 | return relativeFees() != 0 || absoluteFees() != 0;
17 | }
18 |
19 | public static CalculatedFees calculateFor(@Nullable FeesValuesSection feesValues, CachedShop cachedShop) {
20 | if (feesValues == null)
21 | return NO_FEES;
22 |
23 | double absoluteFees;
24 | double relativeFees;
25 |
26 | if (cachedShop.cachedType == ShopType.BUYING) {
27 | absoluteFees = feesValues.absoluteSell;
28 | relativeFees = feesValues.relativeSell;
29 | } else {
30 | absoluteFees = feesValues.absoluteBuy;
31 | relativeFees = feesValues.relativeBuy;
32 | }
33 |
34 | var relativeFeesValue = cachedShop.cachedPrice * (relativeFees / 100);
35 |
36 | if (cachedShop.cachedType == ShopType.BUYING) {
37 | var remainingPrice = cachedShop.cachedPrice - relativeFeesValue;
38 |
39 | if (absoluteFees > remainingPrice) {
40 | absoluteFees = remainingPrice;
41 | }
42 | }
43 |
44 | return new CalculatedFees(absoluteFees, relativeFees, relativeFeesValue);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/DynamicPropertyProvider.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
4 |
5 | public interface DynamicPropertyProvider {
6 |
7 | long getShopDistance(CachedShop cachedShop);
8 |
9 | double getPlayerBalanceForShopCurrency(CachedShop cachedShop);
10 |
11 | double getShopOwnerBalanceForShopCurrency(CachedShop cachedShop);
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/FilteringFunction.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
4 |
5 | @FunctionalInterface
6 | public interface FilteringFunction {
7 |
8 | boolean test(CachedShop shop, DynamicPropertyProvider distanceProvider, boolean negative);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/LimitingFactor.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | public enum LimitingFactor {
4 | PLAYER_SPACE,
5 | PLAYER_STOCK,
6 | SHOP_SPACE,
7 | SHOP_STOCK,
8 | SELLER_FUNDS,
9 | BUYER_FUNDS,
10 | }
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/MaxUnitsResult.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | public record MaxUnitsResult(
4 | int units,
5 | LimitingFactor limitingFactor
6 | ) {}
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/PredicateSelection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | public enum PredicateSelection {
7 | POSITIVE,
8 | NEGATIVE,
9 | INVARIANT
10 | ;
11 |
12 | public static final List values = Arrays.stream(values()).toList();
13 |
14 | public PredicateSelection next() {
15 | return values.get((ordinal() + 1) % values.size());
16 | }
17 |
18 | public static PredicateSelection byOrdinalOrFirst(int ordinal) {
19 | if (ordinal < 0 || ordinal >= values.size())
20 | return values.get(0);
21 |
22 | return values.get(ordinal);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/ResultDisplayData.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
4 | import me.blvckbytes.item_predicate_parser.predicate.ItemPredicate;
5 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
6 | import me.blvckbytes.quick_shop_search.command.SearchFlagsContainer;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | import java.util.Collection;
10 |
11 | public record ResultDisplayData(
12 | Collection shops,
13 | LongOpenHashSet shopIds,
14 | @Nullable ItemPredicate query,
15 | SearchFlagsContainer searchFlagsContainer
16 | ) {
17 | public boolean hasAnyConstraints() {
18 | return searchFlagsContainer.size() > 0 || query != null;
19 | }
20 |
21 | public boolean contains(CachedShop shop) {
22 | return shopIds.contains(shop.handle.getShopId());
23 | }
24 |
25 | public void add(CachedShop shop) {
26 | this.shopIds.add(shop.handle.getShopId());
27 | this.shops.add(shop);
28 | }
29 |
30 | public void remove(CachedShop shop) {
31 | this.shopIds.remove(shop.handle.getShopId());
32 | this.shops.remove(shop);
33 | }
34 |
35 | public boolean doesMatchQuery(CachedShop shop) {
36 | if (query == null)
37 | return true;
38 |
39 | return query.test(shop.handle.getItem());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/SelectionState.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import com.google.gson.JsonObject;
4 | import me.blvckbytes.gpeee.interpreter.EvaluationEnvironmentBuilder;
5 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
6 | import me.blvckbytes.quick_shop_search.PluginPermission;
7 | import me.blvckbytes.quick_shop_search.config.MainSection;
8 | import org.bukkit.entity.Player;
9 |
10 | import java.util.*;
11 |
12 | public class SelectionState {
13 |
14 | private static class SortingCriterionSelection {
15 | final ShopSortingCriteria criterion;
16 | SortingSelection selection;
17 |
18 | SortingCriterionSelection(ShopSortingCriteria criterion, SortingSelection selection) {
19 | this.criterion = criterion;
20 | this.selection = selection;
21 | }
22 | }
23 |
24 | private List sortingSelections;
25 | private int selectedSortingSelectionIndex;
26 |
27 | private Map filteringSelections;
28 | private ShopFilteringCriteria selectedFilteringCriteria;
29 |
30 | public SelectionState() {
31 | resetSorting();
32 | resetFiltering();
33 | }
34 |
35 | private SelectionState(
36 | List sortingSelections,
37 | int selectedSortingSelectionIndex,
38 | Map filteringSelections,
39 | ShopFilteringCriteria selectedFilteringCriteria
40 | ) {
41 | this.sortingSelections = sortingSelections;
42 | this.selectedSortingSelectionIndex = selectedSortingSelectionIndex;
43 | this.filteringSelections = filteringSelections;
44 | this.selectedFilteringCriteria = selectedFilteringCriteria;
45 | }
46 |
47 | public void resetFiltering() {
48 | this.filteringSelections = makeDefaultFilteringSelections();
49 | this.selectedFilteringCriteria = ShopFilteringCriteria.values.get(0);
50 | }
51 |
52 | public void resetSorting() {
53 | this.sortingSelections = makeDefaultSortingSelections();
54 | this.selectedSortingSelectionIndex = 0;
55 | }
56 |
57 | public void nextSortingSelection() {
58 | if (++this.selectedSortingSelectionIndex == this.sortingSelections.size())
59 | this.selectedSortingSelectionIndex = 0;
60 | }
61 |
62 | public void nextSortingOrder() {
63 | var sortingSelection = sortingSelections.get(this.selectedSortingSelectionIndex);
64 | sortingSelection.selection = sortingSelection.selection.next();
65 | }
66 |
67 | public void moveSortingSelectionDown() {
68 | var removalIndex = selectedSortingSelectionIndex;
69 | nextSortingSelection();
70 | var targetSelection = this.sortingSelections.remove(removalIndex);
71 | this.sortingSelections.add(selectedSortingSelectionIndex, targetSelection);
72 | }
73 |
74 | public void nextFilteringCriterion() {
75 | this.selectedFilteringCriteria = this.selectedFilteringCriteria.next();
76 | }
77 |
78 | public void nextFilteringState() {
79 | PredicateSelection currentState = this.filteringSelections.get(this.selectedFilteringCriteria);
80 |
81 | if (currentState == null)
82 | return;
83 |
84 | this.filteringSelections.put(this.selectedFilteringCriteria, currentState.next());
85 | }
86 |
87 | public void applySort(DynamicPropertyProvider distanceProvider, List items) {
88 | items.sort((a, b) -> {
89 | for (var sortingSelection : sortingSelections) {
90 | if (sortingSelection.selection == SortingSelection.INACTIVE)
91 | continue;
92 |
93 | int sortingResult = sortingSelection.criterion.compare(distanceProvider, a, b);
94 |
95 | if (sortingSelection.selection == SortingSelection.DESCENDING)
96 | sortingResult *= -1;
97 |
98 | if (sortingResult != 0)
99 | return sortingResult;
100 | }
101 |
102 | return 0;
103 | });
104 | }
105 |
106 | public List applyFilter(Collection items, DynamicPropertyProvider distanceProvider) {
107 | List result = new ArrayList<>();
108 |
109 | for (var item : items) {
110 | var doesMatch = true;
111 |
112 | for (var entry : this.filteringSelections.entrySet()) {
113 | var predicate = entry.getValue();
114 |
115 | if (predicate == PredicateSelection.INVARIANT)
116 | continue;
117 |
118 | if (entry.getKey().test(item, distanceProvider, predicate == PredicateSelection.NEGATIVE))
119 | continue;
120 |
121 | doesMatch = false;
122 | break;
123 | }
124 |
125 | if (doesMatch)
126 | result.add(item);
127 | }
128 |
129 | return result;
130 | }
131 |
132 | public EvaluationEnvironmentBuilder makeSortingEnvironment(Player player, MainSection mainSection) {
133 | return mainSection.getBaseEnvironment()
134 | .withLiveVariable("sorting_selections", () -> this.sortingSelections)
135 | .withLiveVariable("selected_index", () -> selectedSortingSelectionIndex)
136 | .withLiveVariable("has_permission", () -> PluginPermission.FEATURE_SORT.has(player));
137 | }
138 |
139 | public EvaluationEnvironmentBuilder makeFilteringEnvironment(Player player, MainSection mainSection) {
140 | return mainSection.getBaseEnvironment()
141 | .withLiveVariable("filtering_selections", () -> this.filteringSelections)
142 | .withLiveVariable("selected_criteria", () -> this.selectedFilteringCriteria)
143 | .withLiveVariable("has_permission", () -> PluginPermission.FEATURE_FILTER.has(player));
144 | }
145 |
146 | public JsonObject toJson() {
147 | var result = new JsonObject();
148 |
149 | var sortingSelectionsObject = new JsonObject();
150 |
151 | for (var sortingSelection : sortingSelections)
152 | sortingSelectionsObject.addProperty(String.valueOf(sortingSelection.criterion.ordinal()), String.valueOf(sortingSelection.selection.ordinal()));
153 |
154 | result.add("sortingSelections", sortingSelectionsObject);
155 | result.addProperty("selectedSortingSelectionIndex", selectedSortingSelectionIndex);
156 |
157 | var filteringSelectionsObject = new JsonObject();
158 |
159 | for (var filteringSelection : filteringSelections.entrySet())
160 | filteringSelectionsObject.addProperty(String.valueOf(filteringSelection.getKey().ordinal()), filteringSelection.getValue().ordinal());
161 |
162 | result.add("filteringSelections", filteringSelectionsObject);
163 | result.addProperty("selectedFilteringCriteria", selectedFilteringCriteria.ordinal());
164 |
165 | return result;
166 | }
167 |
168 | public static SelectionState fromJson(JsonObject json) throws Exception {
169 | var sortingSelections = makeDefaultSortingSelections();
170 | var sortingSelectionsObject = json.getAsJsonObject("sortingSelections");
171 |
172 | var sortingSelectionIndex = 0;
173 |
174 | for (var sortingSelectionEntry : sortingSelectionsObject.entrySet()) {
175 | var criterion = ShopSortingCriteria.byOrdinalOrFirst(Integer.parseInt(sortingSelectionEntry.getKey()));
176 | var selection = SortingSelection.byOrdinalOrFirst(sortingSelectionEntry.getValue().getAsInt());
177 |
178 | sortingSelections.removeIf(x -> x.criterion == criterion);
179 | sortingSelections.add(sortingSelectionIndex, new SortingCriterionSelection(criterion, selection));
180 |
181 | ++sortingSelectionIndex;
182 | }
183 |
184 | var filteringSelections = makeDefaultFilteringSelections();
185 | var filteringSelectionsObject = json.getAsJsonObject("filteringSelections");
186 |
187 | for (ShopFilteringCriteria criterion : ShopFilteringCriteria.values) {
188 | filteringSelections.put(
189 | criterion,
190 | PredicateSelection.byOrdinalOrFirst(filteringSelectionsObject.getAsJsonPrimitive(String.valueOf(criterion.ordinal())).getAsInt())
191 | );
192 | }
193 |
194 | var selectedFilteringCriteria = ShopFilteringCriteria.byOrdinalOrFirst(json.getAsJsonPrimitive("selectedFilteringCriteria").getAsInt());
195 | return new SelectionState(
196 | sortingSelections, json.get("selectedSortingSelectionIndex").getAsInt(),
197 | filteringSelections, selectedFilteringCriteria
198 | );
199 | }
200 |
201 | private static ArrayList makeDefaultSortingSelections() {
202 | var result = new ArrayList();
203 |
204 | for (var criterion : ShopSortingCriteria.values)
205 | result.add(new SortingCriterionSelection(criterion, SortingSelection.INACTIVE));
206 |
207 | return result;
208 | }
209 |
210 | private static TreeMap makeDefaultFilteringSelections() {
211 | var result = new TreeMap();
212 |
213 | for (var criteria : ShopFilteringCriteria.values)
214 | result.put(criteria, PredicateSelection.INVARIANT);
215 |
216 | return result;
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/SelectionStateStore.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import com.google.gson.JsonObject;
6 | import me.blvckbytes.quick_shop_search.PluginPermission;
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.plugin.Plugin;
9 |
10 | import java.io.File;
11 | import java.io.FileOutputStream;
12 | import java.io.FileReader;
13 | import java.nio.charset.StandardCharsets;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 | import java.util.UUID;
17 | import java.util.logging.Level;
18 | import java.util.logging.Logger;
19 |
20 | public class SelectionStateStore {
21 |
22 | // JSON seems more than good enough for a state-store this simple and concise
23 | private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
24 |
25 | private final Map states;
26 | private final File stateFile;
27 | private final Logger logger;
28 |
29 | public SelectionStateStore(Plugin plugin, Logger logger) throws Exception {
30 | this.logger = logger;
31 | this.states = new HashMap<>();
32 | this.stateFile = new File(plugin.getDataFolder(), "selection_states.json");
33 |
34 | if (!this.stateFile.exists()) {
35 | if (!this.stateFile.createNewFile())
36 | throw new IllegalStateException("Could not create file " + stateFile);
37 | } else if (!this.stateFile.isFile())
38 | throw new IllegalStateException("Expected file at location " + stateFile);
39 |
40 | this.load();
41 | }
42 |
43 | public SelectionState loadState(Player player) {
44 | var result = this.states.get(player.getUniqueId());
45 |
46 | if (result == null) {
47 | result = new SelectionState();
48 | this.states.put(player.getUniqueId(), result);
49 | return result;
50 | }
51 |
52 | // Make sure that the player's not stuck with any unchangeable sorting- or filtering-setup
53 |
54 | if (!PluginPermission.FEATURE_SORT.has(player))
55 | result.resetSorting();
56 |
57 | if (!PluginPermission.FEATURE_FILTER.has(player))
58 | result.resetFiltering();
59 |
60 | return result;
61 | }
62 |
63 | public void onShutdown() {
64 | try {
65 | var stateData = new JsonObject();
66 |
67 | for (var state : this.states.entrySet())
68 | stateData.add(state.getKey().toString(), state.getValue().toJson());
69 |
70 | try (
71 | var outputStream = new FileOutputStream(stateFile)
72 | ) {
73 | outputStream.write(gson.toJson(stateData).getBytes(StandardCharsets.UTF_8));
74 | }
75 | } catch (Exception e) {
76 | logger.log(Level.SEVERE, "Could not write state-file to " + stateFile, e);
77 | }
78 | }
79 |
80 | private void load() throws Exception {
81 | var stateData = gson.fromJson(new FileReader(stateFile), JsonObject.class);
82 |
83 | if (stateData == null)
84 | return;
85 |
86 | for (var entry : stateData.entrySet()) {
87 | var uuid = UUID.fromString(entry.getKey());
88 |
89 | if (!(entry.getValue() instanceof JsonObject object))
90 | continue;
91 |
92 | SelectionState state;
93 |
94 | try {
95 | state = SelectionState.fromJson(object);
96 | } catch (Exception e) {
97 | logger.warning("Could not load selection state: " + e.getMessage() + "; falling back to defaults");
98 | state = new SelectionState();
99 | }
100 |
101 | this.states.put(uuid, state);
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/ShopFilteringCriteria.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | public enum ShopFilteringCriteria implements FilteringFunction {
9 |
10 | IS_BUYING((shop, d, negative) -> shop.handle.isBuying() ^ negative),
11 | IS_SELLING((shop, d, negative) -> shop.handle.isSelling() ^ negative),
12 | IS_UNLIMITED((shop, d, negative) -> shop.handle.isUnlimited() ^ negative),
13 | HAS_STOCK_LEFT((shop, d, negative) -> {
14 | if (!shop.handle.isSelling())
15 | return true;
16 |
17 | return (shop.handle.isUnlimited() || shop.cachedStock > 0) ^ negative;
18 | }),
19 | HAS_SPACE_LEFT((shop, d, negative) -> {
20 | if (!shop.handle.isBuying())
21 | return true;
22 |
23 | return (shop.handle.isUnlimited() || shop.cachedSpace > 0) ^ negative;
24 | }),
25 | SAME_WORLD((shop, d, negative) -> (d.getShopDistance(shop) >= 0) ^ negative),
26 | CAN_BUY((shop, d, negative) -> {
27 | if (shop.handle.isSelling())
28 | return (d.getPlayerBalanceForShopCurrency(shop) >= shop.handle.getPrice()) ^ negative;
29 |
30 | return (d.getShopOwnerBalanceForShopCurrency(shop) >= shop.handle.getPrice()) ^ negative;
31 | })
32 | ;
33 |
34 | private final FilteringFunction predicate;
35 |
36 | public static final List values = Arrays.stream(values()).toList();
37 |
38 | ShopFilteringCriteria(FilteringFunction predicate) {
39 | this.predicate = predicate;
40 | }
41 |
42 | @Override
43 | public boolean test(CachedShop shop, DynamicPropertyProvider distanceProvider, boolean negative) {
44 | return predicate.test(shop, distanceProvider, negative);
45 | }
46 |
47 | public ShopFilteringCriteria next() {
48 | return values.get((ordinal() + 1) % values.size());
49 | }
50 |
51 | public static ShopFilteringCriteria byOrdinalOrFirst(int ordinal) {
52 | if (ordinal < 0 || ordinal >= values.size())
53 | return values.get(0);
54 |
55 | return values.get(ordinal);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/ShopSortingCriteria.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | public enum ShopSortingCriteria implements SortingFunction {
10 |
11 | PRICE((d, a, b) -> Double.compare(a.handle.getPrice(), b.handle.getPrice())),
12 | OWNER_NAME((d, a, b) -> a.handle.getOwner().getDisplay().compareTo(b.handle.getOwner().getDisplay())),
13 | STOCK_LEFT((d, a, b) -> Integer.compare(a.cachedStock, b.cachedStock)),
14 | SPACE_LEFT((d, a, b) -> Integer.compare(a.cachedSpace, b.cachedSpace)),
15 | ITEM_TYPE((d, a, b) -> a.handle.getItem().getType().compareTo(b.handle.getItem().getType())),
16 | SHOP_TYPE((d, a, b) -> a.handle.getShopType().compareTo(b.handle.getShopType())),
17 | SHOP_NAME((d, a, b) -> compareNullableStrings(a.handle.getShopName(), b.handle.getShopName())),
18 | DISTANCE((d, a, b) -> Long.compare(d.getShopDistance(a), d.getShopDistance(b))),
19 | WORLD_NAME((d, a, b) -> a.shopWorldName.compareTo(b.shopWorldName)),
20 | ;
21 |
22 | private final SortingFunction function;
23 |
24 | public static final List values = Arrays.stream(values()).toList();
25 |
26 | ShopSortingCriteria(SortingFunction function) {
27 | this.function = function;
28 | }
29 |
30 | @Override
31 | public int compare(DynamicPropertyProvider distanceProvider, CachedShop a, CachedShop b) {
32 | return function.compare(distanceProvider, a, b);
33 | }
34 |
35 | public static ShopSortingCriteria byOrdinalOrFirst(int ordinal) {
36 | if (ordinal < 0 || ordinal >= values.size())
37 | return values.get(0);
38 |
39 | return values.get(ordinal);
40 | }
41 |
42 | private static int compareNullableStrings(@Nullable String a, @Nullable String b) {
43 | if (a == null && b == null)
44 | return 0;
45 |
46 | if (a == null)
47 | return 1;
48 |
49 | if (b == null)
50 | return -1;
51 |
52 | return a.compareTo(b);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/SortingFunction.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import me.blvckbytes.quick_shop_search.cache.CachedShop;
4 |
5 | @FunctionalInterface
6 | public interface SortingFunction {
7 |
8 | int compare(DynamicPropertyProvider distanceProvider, CachedShop a, CachedShop b);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/SortingSelection.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | public enum SortingSelection {
7 | ASCENDING,
8 | DESCENDING,
9 | INACTIVE
10 | ;
11 |
12 | public static final List values = Arrays.stream(values()).toList();
13 |
14 | public SortingSelection next() {
15 | return values.get((ordinal() + 1) % values.size());
16 | }
17 |
18 | public static SortingSelection byOrdinalOrFirst(int ordinal) {
19 | if (ordinal < 0 || ordinal >= values.size())
20 | return values.get(0);
21 |
22 | return values.get(ordinal);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/result/TeleportCooldownType.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.result;
2 |
3 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
4 | import me.blvckbytes.quick_shop_search.PluginPermission;
5 | import org.jetbrains.annotations.Nullable;
6 |
7 | public record TeleportCooldownType(
8 | PluginPermission bypassPermission,
9 | String stampKey,
10 | long durationMillis,
11 | @Nullable BukkitEvaluable cooldownMessage
12 | ) {}
13 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/teleport/TeleportDisplay.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.teleport;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
5 | import me.blvckbytes.quick_shop_search.config.MainSection;
6 | import me.blvckbytes.quick_shop_search.display.Display;
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.inventory.Inventory;
9 |
10 | public class TeleportDisplay extends Display {
11 |
12 | public TeleportDisplay(
13 | Player player,
14 | TeleportDisplayData displayData,
15 | ConfigKeeper config,
16 | PlatformScheduler scheduler
17 | ) {
18 | super(player, displayData, config, scheduler);
19 |
20 | show();
21 | }
22 |
23 | @Override
24 | protected void renderItems() {
25 | config.rootSection.teleportDisplay.items.filler.renderInto(inventory, displayData.extendedShopEnvironment());
26 | config.rootSection.teleportDisplay.items.back.renderInto(inventory, displayData.extendedShopEnvironment());
27 | config.rootSection.teleportDisplay.items.shopLocation.renderInto(inventory, displayData.extendedShopEnvironment());
28 |
29 | // The nearest player-warp icon may need to make a network-call in order to receive the owner's textures
30 | // Do not stall bringing up the rest of the UI this way
31 | scheduler.runAsync(task -> config.rootSection.teleportDisplay.items.nearestPlayerWarpLocation.renderInto(inventory, displayData.extendedShopEnvironment()));
32 |
33 | config.rootSection.teleportDisplay.items.nearestEssentialsWarpLocation.renderInto(inventory, displayData.extendedShopEnvironment());
34 | }
35 |
36 | @Override
37 | protected Inventory makeInventory() {
38 | return config.rootSection.teleportDisplay.createInventory(displayData.extendedShopEnvironment());
39 | }
40 |
41 | @Override
42 | public void onConfigReload() {
43 | show();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/teleport/TeleportDisplayData.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.teleport;
2 |
3 | import me.blvckbytes.gpeee.interpreter.IEvaluationEnvironment;
4 | import me.blvckbytes.quick_shop_search.integration.essentials_warps.EssentialsWarpData;
5 | import me.blvckbytes.quick_shop_search.integration.player_warps.PlayerWarpData;
6 | import org.bukkit.Location;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | public record TeleportDisplayData(
10 | Location finalShopLocation,
11 | IEvaluationEnvironment extendedShopEnvironment,
12 | @Nullable PlayerWarpData nearestPlayerWarp,
13 | @Nullable EssentialsWarpData nearestEssentialsWarp,
14 | Runnable reopenResultDisplay,
15 | @Nullable Runnable afterTeleporting
16 | ) {}
17 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/display/teleport/TeleportDisplayHandler.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.display.teleport;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 | import me.blvckbytes.bukkitevaluable.BukkitEvaluable;
5 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
6 | import me.blvckbytes.quick_shop_search.PluginPermission;
7 | import me.blvckbytes.quick_shop_search.SlowTeleportManager;
8 | import me.blvckbytes.quick_shop_search.config.MainSection;
9 | import me.blvckbytes.quick_shop_search.display.DisplayHandler;
10 | import org.bukkit.entity.Player;
11 | import org.bukkit.event.inventory.ClickType;
12 |
13 | public class TeleportDisplayHandler extends DisplayHandler {
14 |
15 | private final SlowTeleportManager slowTeleportManager;
16 |
17 | public TeleportDisplayHandler(
18 | ConfigKeeper config,
19 | PlatformScheduler scheduler,
20 | SlowTeleportManager slowTeleportManager
21 | ) {
22 | super(config, scheduler);
23 |
24 | this.slowTeleportManager = slowTeleportManager;
25 | }
26 |
27 | @Override
28 | public TeleportDisplay instantiateDisplay(Player player, TeleportDisplayData displayData) {
29 | return new TeleportDisplay(player, displayData, config, scheduler);
30 | }
31 |
32 | @Override
33 | protected void handleClick(Player player, TeleportDisplay display, ClickType clickType, int slot) {
34 | if (clickType != ClickType.LEFT)
35 | return;
36 |
37 | if (config.rootSection.teleportDisplay.items.back.getDisplaySlots().contains(slot)) {
38 | display.displayData.reopenResultDisplay().run();
39 | return;
40 | }
41 |
42 | BukkitEvaluable message;
43 |
44 | if (config.rootSection.teleportDisplay.items.shopLocation.getDisplaySlots().contains(slot)) {
45 | directlyTeleportToShopLocation(player, display.displayData);
46 | return;
47 | }
48 |
49 | if (config.rootSection.teleportDisplay.items.nearestPlayerWarpLocation.getDisplaySlots().contains(slot)) {
50 | var nearestPlayerWarp = display.displayData.nearestPlayerWarp();
51 |
52 | if (nearestPlayerWarp == null)
53 | return;
54 |
55 | scheduler.runAtEntity(player, scheduleTask -> player.closeInventory());
56 |
57 | if (nearestPlayerWarp.isBanned() && !PluginPermission.FEATURE_TELEPORT_NEAREST_PLAYER_WARP_BAN_BYPASS.has(player)) {
58 | if ((message = config.rootSection.playerMessages.nearestPlayerWarpBanned) != null)
59 | message.sendMessage(player, display.displayData.extendedShopEnvironment());
60 |
61 | return;
62 | }
63 |
64 | if ((message = config.rootSection.playerMessages.beforeTeleportingNearestPlayerWarp) != null)
65 | message.sendMessage(player, config.rootSection.getBaseEnvironment().build(display.displayData.extendedShopEnvironment()));
66 |
67 | slowTeleportManager.initializeTeleportation(player, nearestPlayerWarp.location(), () -> {
68 | if (display.displayData.afterTeleporting() != null)
69 | display.displayData.afterTeleporting().run();
70 | });
71 |
72 | return;
73 | }
74 |
75 | if (config.rootSection.teleportDisplay.items.nearestEssentialsWarpLocation.getDisplaySlots().contains(slot)) {
76 | var nearestEssentialsWarp = display.displayData.nearestEssentialsWarp();
77 |
78 | if (nearestEssentialsWarp == null)
79 | return;
80 |
81 | scheduler.runAtEntity(player, scheduleTask -> player.closeInventory());
82 |
83 | if ((message = config.rootSection.playerMessages.beforeTeleportingNearestEssentialsWarp) != null)
84 | message.sendMessage(player, config.rootSection.getBaseEnvironment().build(display.displayData.extendedShopEnvironment()));
85 |
86 | slowTeleportManager.initializeTeleportation(player, nearestEssentialsWarp.location(), () -> {
87 | if (display.displayData.afterTeleporting() != null)
88 | display.displayData.afterTeleporting().run();
89 | });
90 | }
91 | }
92 |
93 | public void directlyTeleportToShopLocation(Player player, TeleportDisplayData displayData) {
94 | scheduler.runAtEntity(player, scheduleTask -> player.closeInventory());
95 |
96 | BukkitEvaluable message;
97 |
98 | if ((message = config.rootSection.playerMessages.beforeTeleporting) != null)
99 | message.sendMessage(player, config.rootSection.getBaseEnvironment().build(displayData.extendedShopEnvironment()));
100 |
101 | slowTeleportManager.initializeTeleportation(player, displayData.finalShopLocation(), () -> {
102 | if (displayData.afterTeleporting() != null)
103 | displayData.afterTeleporting().run();
104 | });
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/core/src/main/java/me/blvckbytes/quick_shop_search/integration/IntegrationRegistry.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
5 | import me.blvckbytes.bukkitevaluable.ReloadPriority;
6 | import me.blvckbytes.quick_shop_search.config.MainSection;
7 | import me.blvckbytes.quick_shop_search.integration.essentials_warps.EssentialsWarpsIntegration;
8 | import me.blvckbytes.quick_shop_search.integration.essentials_warps.IEssentialsWarpsIntegration;
9 | import me.blvckbytes.quick_shop_search.integration.player_warps.IPlayerWarpsIntegration;
10 | import me.blvckbytes.quick_shop_search.integration.player_warps.OlzieDevPlayerWarpsIntegration;
11 | import me.blvckbytes.quick_shop_search.integration.player_warps.RevivaloPlayerWarpsIntegration;
12 | import me.blvckbytes.quick_shop_search.integration.worldguard.IWorldGuardIntegration;
13 | import me.blvckbytes.quick_shop_search.integration.worldguard.WorldGuardIntegration;
14 | import org.bukkit.Bukkit;
15 | import org.bukkit.plugin.Plugin;
16 | import org.jetbrains.annotations.Nullable;
17 |
18 | import java.util.logging.Level;
19 | import java.util.logging.Logger;
20 |
21 | public class IntegrationRegistry {
22 |
23 | private final ConfigKeeper config;
24 | private final Logger logger;
25 | private final PlatformScheduler scheduler;
26 | private final Plugin plugin;
27 |
28 | private @Nullable IEssentialsWarpsIntegration essentialsWarpsIntegration;
29 | private @Nullable IPlayerWarpsIntegration playerWarpsIntegration;
30 | private @Nullable IWorldGuardIntegration worldGuardIntegration;
31 |
32 | public IntegrationRegistry(
33 | ConfigKeeper config,
34 | Logger logger,
35 | PlatformScheduler scheduler,
36 | Plugin plugin
37 | ) {
38 | this.config = config;
39 | this.logger = logger;
40 | this.scheduler = scheduler;
41 | this.plugin = plugin;
42 |
43 | this.loadIntegrations();
44 |
45 | config.registerReloadListener(this::loadIntegrations, ReloadPriority.HIGHEST);
46 | }
47 |
48 | public @Nullable IWorldGuardIntegration getWorldGuardIntegration() {
49 | if (!config.rootSection.worldGuardIntegration.enabled)
50 | return null;
51 |
52 | return this.worldGuardIntegration;
53 | }
54 |
55 | public @Nullable IEssentialsWarpsIntegration getEssentialsWarpsIntegration() {
56 | if (!config.rootSection.essentialsWarpsIntegration.enabled)
57 | return null;
58 |
59 | return this.essentialsWarpsIntegration;
60 | }
61 |
62 | public @Nullable IPlayerWarpsIntegration getPlayerWarpsIntegration() {
63 | if (!config.rootSection.playerWarpsIntegration.enabled)
64 | return null;
65 |
66 | return this.playerWarpsIntegration;
67 | }
68 |
69 | private void loadIntegrations() {
70 | if (this.worldGuardIntegration == null)
71 | this.worldGuardIntegration = tryLoadWorldGuardIntegration();
72 |
73 | if (this.essentialsWarpsIntegration == null)
74 | this.essentialsWarpsIntegration = tryLoadEssentialsWarpsIntegration();
75 |
76 | if (this.playerWarpsIntegration == null)
77 | this.playerWarpsIntegration = tryLoadPlayerWarpsIntegration();
78 | }
79 |
80 | private @Nullable IWorldGuardIntegration tryLoadWorldGuardIntegration() {
81 | if (!config.rootSection.worldGuardIntegration.enabled)
82 | return null;
83 |
84 | if (!Bukkit.getPluginManager().isPluginEnabled("WorldGuard")) {
85 | logger.warning("WorldGuard-Integration is enabled, but the corresponding plugin could not be located!");
86 | return null;
87 | }
88 |
89 | if (this.worldGuardIntegration != null)
90 | return this.worldGuardIntegration;
91 |
92 | try {
93 | var worldGuardIntegration = new WorldGuardIntegration(logger, config);
94 | logger.info("Successfully loaded the WorldGuard-integration!");
95 | return worldGuardIntegration;
96 | } catch (Throwable e) {
97 | logger.log(Level.SEVERE, "Could not load the WorldGuard-integration!", e);
98 | }
99 |
100 | return null;
101 | }
102 |
103 | private @Nullable IEssentialsWarpsIntegration tryLoadEssentialsWarpsIntegration() {
104 | if (!config.rootSection.essentialsWarpsIntegration.enabled)
105 | return null;
106 |
107 | if (!Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
108 | logger.warning("Essentials-Warps-Integration is enabled, but the corresponding plugin could not be located!");
109 | return null;
110 | }
111 |
112 | if (this.essentialsWarpsIntegration != null)
113 | return this.essentialsWarpsIntegration;
114 |
115 | try {
116 | var essentialsWarpsIntegration = new EssentialsWarpsIntegration(logger, scheduler, worldGuardIntegration, config);
117 | Bukkit.getPluginManager().registerEvents(essentialsWarpsIntegration, plugin);
118 | logger.info("Successfully loaded the Essentials-Warps-integration!");
119 | return essentialsWarpsIntegration;
120 | } catch (Throwable e) {
121 | logger.log(Level.SEVERE, "Could not load the Essentials-Warps-integration!", e);
122 | }
123 |
124 | return null;
125 | }
126 |
127 | private @Nullable IPlayerWarpsIntegration tryLoadPlayerWarpsIntegration() {
128 | IPlayerWarpsIntegration integration;
129 |
130 | if (!config.rootSection.playerWarpsIntegration.enabled)
131 | return null;
132 |
133 | if (!Bukkit.getPluginManager().isPluginEnabled("PlayerWarps")) {
134 | logger.warning("PlayerWarps-Integration is enabled, but the corresponding plugin could not be located!");
135 | return null;
136 | }
137 |
138 | if (this.playerWarpsIntegration != null)
139 | return this.playerWarpsIntegration;
140 |
141 | try {
142 | integration = new OlzieDevPlayerWarpsIntegration(logger, scheduler, config, worldGuardIntegration);
143 | Bukkit.getPluginManager().registerEvents(integration, plugin);
144 | logger.info("Successfully loaded the com.olziedev PlayerWarps-integration!");
145 | return integration;
146 | } catch (Throwable firstError) {
147 | try {
148 | integration = new RevivaloPlayerWarpsIntegration(logger, scheduler, config, worldGuardIntegration);
149 | logger.info("Successfully loaded the dev.revivalo PlayerWarps-integration!");
150 | return integration;
151 | } catch (Throwable secondError) {
152 | logger.log(Level.SEVERE, "Could not load the dev.revivalo PlayerWarps-integration!", secondError);
153 | }
154 |
155 | logger.log(Level.SEVERE, "Could not load the com.olziedev PlayerWarps-integration!", firstError);
156 | }
157 |
158 | return null;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/core/src/main/resources/plugin.yml:
--------------------------------------------------------------------------------
1 | name: QuickShopSearch
2 | main: me.blvckbytes.quick_shop_search.QuickShopSearchPlugin
3 | version: ${project.version}
4 | author: BlvckBytes
5 | api-version: 1.18
6 | depend: [QuickShop-Hikari, ItemPredicateParser]
7 | softdepend: [PlayerWarps, Essentials, WorldGuard]
8 | folia-supported: true
9 | commands:
10 | quickshopsearch:
11 | description: 'Query shops using the main predicate language'
12 | quickshopsearchlanguage:
13 | description: 'Query shops using any supported predicate language'
14 | quickshopsearchreload:
15 | description: 'Reload the configuration-file'
16 | permissions:
17 | quickshopsearch.command.qss:
18 | description: 'Invoke the main command'
19 | default: op
20 | quickshopsearch.command.qssl:
21 | description: 'Invoke the language command'
22 | default: op
23 | quickshopsearch.command.advertise:
24 | description: 'Invoke the advertise sub-command of QuickShop'
25 | default: op
26 | quickshopsearch.command.advertise.owner-bypass:
27 | description: 'Bypass having to be the owner of a shop to toggle advertising'
28 | default: op
29 | quickshopsearch.command.qssrl:
30 | description: 'Invoke the reload command'
31 | default: op
32 | quickshopsearch.empty-predicate:
33 | description: 'Query all shops without a predicate'
34 | default: op
35 | quickshopsearch.bypass-non-advertise:
36 | description: 'View non-advertising shops'
37 | default: op
38 | quickshopsearch.bypass-slow-teleport:
39 | description: 'Bypass having to wait for the teleportation-countdown'
40 | default: op
41 | quickshopsearch.bypass-access-lists:
42 | description: 'Bypass the permission-specific restrictions of access-lists'
43 | default: op
44 | quickshopsearch.access-list.:
45 | description: 'Access the permission-guarded access-list with a name of '
46 | default: op
47 | quickshopsearch.teleport-cooldown.:
48 | description: 'Access the permission-guarded teleport-cooldown with a group-name of '
49 | default: op
50 | quickshopsearch.other-world:
51 | description: 'Whether to display shops which are not in the same world as the player'
52 | default: op
53 | quickshopsearch.feature.sort:
54 | description: 'Use the sorting feature'
55 | default: op
56 | quickshopsearch.feature.filter:
57 | description: 'Use the filtering feature'
58 | default: op
59 | quickshopsearch.feature.teleport:
60 | description: 'Teleport to shops within the same world'
61 | default: op
62 | quickshopsearch.feature.teleport.bypass-cooldown.same-shop:
63 | description: 'Bypass the teleport cooldown for the same shop within the player''s world'
64 | default: op
65 | quickshopsearch.feature.teleport.bypass-cooldown.other-shop:
66 | description: 'Bypass the teleport cooldown for any shop within the player''s world'
67 | default: op
68 | quickshopsearch.feature.teleport.other-world:
69 | description: 'Teleport to shops within another world'
70 | default: op
71 | quickshopsearch.feature.teleport.other-world.bypass-cooldown.same-shop:
72 | description: 'Bypass the teleport cooldown for the same shop within a foreign world'
73 | default: op
74 | quickshopsearch.feature.teleport.other-world.bypass-cooldown.other-shop:
75 | description: 'Bypass the teleport cooldown for any shop within a foreign world'
76 | default: op
77 | quickshopsearch.feature.interact:
78 | description: 'Remotely interact with shops within the same world '
79 | default: op
80 | quickshopsearch.feature.interact.other-world:
81 | description: 'Remotely interact with shops within another world '
82 | default: op
83 | quickshopsearch.feature.teleport.nearest-player-warp.ban-bypass:
84 | description: 'Bypass player-warp bans when teleporting'
85 | default: op
86 | quickshopsearch.feature.fees.bypass:
87 | description: 'Bypass fees within the same world'
88 | default: op
89 | quickshopsearch.feature.fees.bypass.other-world:
90 | description: 'Bypass fees within another world'
91 | default: op
92 | quickshopsearch.feature.fees.permission-name.:
93 | description: 'Access permission-guarded fees with a name of '
94 | default: op
95 | quickshopsearch.command.advertise.allowlist-bypass:
96 | description: 'Bypass the allow-list of regions when advertising owned shops'
97 | default: op
98 | quickshopsearch.command.advertise.allowlist-bypass.others:
99 | description: 'Bypass the allow-list of regions when advertising foreign shops'
100 | default: op
--------------------------------------------------------------------------------
/integration/essentials_warps_integration/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | me.blvckbytes
9 | quickshopsearch-root
10 | ${revision}
11 | ../../pom.xml
12 |
13 |
14 | essentials_warps_integration
15 |
16 |
17 | 17
18 | 17
19 | UTF-8
20 |
21 |
22 |
23 |
24 | essentialsX
25 | https://repo.essentialsx.net/releases/
26 |
27 |
28 |
29 |
30 |
31 | net.essentialsx
32 | EssentialsX
33 | 2.20.1
34 | provided
35 |
36 |
37 | *
38 | *
39 |
40 |
41 |
42 |
43 | me.blvckbytes
44 | common
45 | ${revision}
46 |
47 |
48 |
--------------------------------------------------------------------------------
/integration/essentials_warps_integration/src/main/java/me/blvckbytes/quick_shop_search/integration/essentials_warps/EssentialsWarpsIntegration.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.essentials_warps;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
5 | import me.blvckbytes.quick_shop_search.config.MainSection;
6 | import me.blvckbytes.quick_shop_search.integration.ChunkBucketedCache;
7 | import me.blvckbytes.quick_shop_search.integration.worldguard.IWorldGuardIntegration;
8 | import net.ess3.api.IEssentials;
9 | import net.essentialsx.api.v2.events.WarpModifyEvent;
10 | import org.bukkit.Bukkit;
11 | import org.bukkit.Location;
12 | import org.bukkit.entity.Player;
13 | import org.bukkit.event.EventHandler;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.util.logging.Level;
17 | import java.util.logging.Logger;
18 |
19 | public class EssentialsWarpsIntegration extends ChunkBucketedCache implements IEssentialsWarpsIntegration {
20 |
21 | private final ConfigKeeper config;
22 | private final @Nullable IWorldGuardIntegration worldGuardIntegration;
23 |
24 | public EssentialsWarpsIntegration(
25 | Logger logger,
26 | PlatformScheduler scheduler,
27 | @Nullable IWorldGuardIntegration worldGuardIntegration,
28 | ConfigKeeper config
29 | ) {
30 | this.worldGuardIntegration = worldGuardIntegration;
31 | this.config = config;
32 |
33 | if (!(Bukkit.getPluginManager().getPlugin("Essentials") instanceof IEssentials essentials))
34 | throw new IllegalStateException("Expected the essentials-plugin to be an instance of IEssentials");
35 |
36 | try {
37 | Class.forName("net.essentialsx.api.v2.events.WarpModifyEvent");
38 | } catch (ClassNotFoundException e) {
39 | throw new IllegalStateException("Expected the WarpModifyEvent of Essentials to be existing; update your software!");
40 | }
41 |
42 | scheduler.runAsync(task -> {
43 | var warpsApi = essentials.getWarps();
44 | var warpNamesList = warpsApi.getList();
45 |
46 | for (var warpName : warpNamesList) {
47 | try {
48 | var warpLocation = warpsApi.getWarp(warpName);
49 | registerItem(new EssentialsWarpData(warpName, warpLocation));
50 | } catch (Exception e) {
51 | logger.log(Level.SEVERE, "Could not resolve warp by name \"" + warpName + "\"", e);
52 | }
53 | }
54 |
55 | logger.info("Registered " + warpNamesList.size() + " Essentials-Warps!");
56 | });
57 | }
58 |
59 | @Override
60 | protected @Nullable Location extractItemLocation(EssentialsWarpData item) {
61 | return item.location();
62 | }
63 |
64 | @Override
65 | protected boolean doItemsEqual(EssentialsWarpData a, EssentialsWarpData b) {
66 | return a.name().equals(b.name());
67 | }
68 |
69 | @EventHandler
70 | public void onWarpModify(WarpModifyEvent event) {
71 | var warpName = event.getWarpName();
72 |
73 | if (event.getCause() == WarpModifyEvent.WarpModifyCause.DELETE) {
74 | unregisterItem(new EssentialsWarpData(warpName, event.getOldLocation()));
75 | return;
76 | }
77 |
78 | if (event.getCause() == WarpModifyEvent.WarpModifyCause.CREATE) {
79 | registerItem(new EssentialsWarpData(warpName, event.getNewLocation()));
80 | return;
81 | }
82 |
83 | if (event.getCause() == WarpModifyEvent.WarpModifyCause.UPDATE) {
84 | unregisterItem(new EssentialsWarpData(warpName, event.getOldLocation()));
85 | registerItem(new EssentialsWarpData(warpName, event.getNewLocation()));
86 | }
87 | }
88 |
89 | @Override
90 | public @Nullable EssentialsWarpData locateNearestWithinRange(Player player, Location origin, int blockRadius) {
91 | var matchPredicate = IWorldGuardIntegration.makePredicate(origin, worldGuardIntegration, config.rootSection.essentialsWarpsIntegration.withinSameRegion);
92 | return findClosestItem(origin, blockRadius, matchPredicate);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/integration/olziedev_player_warps_integration/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | me.blvckbytes
9 | quickshopsearch-root
10 | ${revision}
11 | ../../pom.xml
12 |
13 |
14 | olziedev_player_warps_integration
15 |
16 |
17 | 17
18 | 17
19 | UTF-8
20 |
21 |
22 |
23 |
24 | olzie-repo
25 | https://repo.olziedev.com/
26 |
27 |
28 |
29 |
30 |
31 | com.olziedev
32 | playerwarps-api
33 | 7.7.1
34 | provided
35 |
36 |
37 | me.blvckbytes
38 | common
39 | ${revision}
40 |
41 |
42 |
--------------------------------------------------------------------------------
/integration/olziedev_player_warps_integration/src/main/java/me/blvckbytes/quick_shop_search/integration/player_warps/OlzieDevPlayerWarpsIntegration.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.player_warps;
2 |
3 | import com.olziedev.playerwarps.api.PlayerWarpsAPI;
4 | import com.olziedev.playerwarps.api.events.warp.PlayerWarpCreateEvent;
5 | import com.olziedev.playerwarps.api.events.warp.PlayerWarpRemoveEvent;
6 | import com.olziedev.playerwarps.api.warp.Warp;
7 | import com.tcoded.folialib.impl.PlatformScheduler;
8 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
9 | import me.blvckbytes.quick_shop_search.config.MainSection;
10 | import me.blvckbytes.quick_shop_search.integration.ChunkBucketedCache;
11 | import me.blvckbytes.quick_shop_search.integration.worldguard.IWorldGuardIntegration;
12 | import org.bukkit.Location;
13 | import org.bukkit.entity.Player;
14 | import org.bukkit.event.EventHandler;
15 | import org.bukkit.event.EventPriority;
16 |
17 | import javax.annotation.Nullable;
18 | import java.util.logging.Logger;
19 |
20 | public class OlzieDevPlayerWarpsIntegration extends ChunkBucketedCache implements IPlayerWarpsIntegration {
21 |
22 | private final ConfigKeeper config;
23 | private final @Nullable IWorldGuardIntegration worldGuardIntegration;
24 |
25 | public OlzieDevPlayerWarpsIntegration(
26 | Logger logger,
27 | PlatformScheduler scheduler,
28 | ConfigKeeper config,
29 | @Nullable IWorldGuardIntegration worldGuardIntegration
30 | ) {
31 | this.config = config;
32 | this.worldGuardIntegration = worldGuardIntegration;
33 |
34 | PlayerWarpsAPI.getInstance(instance -> {
35 | scheduler.runAsync(task -> {
36 | var warps = PlayerWarpsAPI.getInstance().getPlayerWarps(false);
37 |
38 | for (var playerWarp : warps)
39 | registerItem(playerWarp);
40 |
41 | logger.info("Registered " + warps.size() + " PlayerWarps!");
42 | });
43 | });
44 | }
45 |
46 | @Override
47 | public @Nullable PlayerWarpData locateNearestWithinRange(Player player, Location origin, int blockRadius) {
48 | var matchPredicate = IWorldGuardIntegration.makePredicate(origin, worldGuardIntegration, config.rootSection.playerWarpsIntegration.withinSameRegion);
49 | var nearestWarp = findClosestItem(origin, blockRadius, matchPredicate);
50 |
51 | if (nearestWarp == null)
52 | return null;
53 |
54 | var playerId = player.getUniqueId();
55 | var isBanned = false;
56 |
57 | for (var banned : nearestWarp.getBanned()) {
58 | if (banned.getUUID().equals(playerId)) {
59 | isBanned = true;
60 | break;
61 | }
62 | }
63 |
64 | return new PlayerWarpData(
65 | nearestWarp.getWarpPlayer().getName(),
66 | nearestWarp.getWarpName(),
67 | nearestWarp.getWarpLocation().getLocation(),
68 | isBanned
69 | );
70 | }
71 |
72 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
73 | public void onPlayerWarpCreate(PlayerWarpCreateEvent event) {
74 | registerItem(event.getPlayerWarp());
75 | }
76 |
77 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
78 | public void onPlayerWarpRemove(PlayerWarpRemoveEvent event) {
79 | unregisterItem(event.getPlayerWarp());
80 | }
81 |
82 | @Override
83 | protected @Nullable Location extractItemLocation(Warp item) {
84 | return item.getWarpLocation().getLocation();
85 | }
86 |
87 | @Override
88 | protected boolean doItemsEqual(Warp a, Warp b) {
89 | return a.getUUID().equals(b.getUUID());
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/integration/revivalo_player_warps_integration/libs/PlayerWarps-3.3.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlvckBytes/QuickShopSearch/e3e2d2ac219b935e09dbaf6d508f33a1258ab361/integration/revivalo_player_warps_integration/libs/PlayerWarps-3.3.2.jar
--------------------------------------------------------------------------------
/integration/revivalo_player_warps_integration/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | me.blvckbytes
9 | quickshopsearch-root
10 | ${revision}
11 | ../../pom.xml
12 |
13 |
14 | revivalo_player_warps_integration
15 |
16 |
17 | 17
18 | 17
19 | UTF-8
20 |
21 |
22 |
23 |
24 | dev.revivalo
25 | player-warps
26 | 3.3.2
27 | system
28 | ${project.basedir}/libs/PlayerWarps-3.3.2.jar
29 |
30 |
31 | me.blvckbytes
32 | common
33 | ${revision}
34 |
35 |
36 |
--------------------------------------------------------------------------------
/integration/revivalo_player_warps_integration/src/main/java/me/blvckbytes/quick_shop_search/integration/player_warps/RevivaloPlayerWarpsIntegration.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.player_warps;
2 |
3 | import com.tcoded.folialib.impl.PlatformScheduler;
4 | import dev.revivalo.playerwarps.PlayerWarpsPlugin;
5 | import dev.revivalo.playerwarps.warp.Warp;
6 | import dev.revivalo.playerwarps.warp.WarpManager;
7 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
8 | import me.blvckbytes.quick_shop_search.config.MainSection;
9 | import me.blvckbytes.quick_shop_search.integration.ChunkBucketedCache;
10 | import me.blvckbytes.quick_shop_search.integration.worldguard.IWorldGuardIntegration;
11 | import org.bukkit.Bukkit;
12 | import org.bukkit.Location;
13 | import org.bukkit.entity.Player;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.util.logging.Logger;
17 |
18 | public class RevivaloPlayerWarpsIntegration extends ChunkBucketedCache implements IPlayerWarpsIntegration {
19 |
20 | private final @Nullable IWorldGuardIntegration worldGuardIntegration;
21 | private final ConfigKeeper config;
22 | private final PlatformScheduler platformScheduler;
23 |
24 | private final WarpManager warpManager;
25 |
26 | public RevivaloPlayerWarpsIntegration(
27 | Logger logger,
28 | PlatformScheduler platformScheduler,
29 | ConfigKeeper config,
30 | @Nullable IWorldGuardIntegration worldGuardIntegration
31 | ) {
32 | this.config = config;
33 | this.platformScheduler = platformScheduler;
34 | this.worldGuardIntegration = worldGuardIntegration;
35 |
36 | this.warpManager = PlayerWarpsPlugin.getWarpHandler();
37 |
38 | if (this.warpManager == null)
39 | throw new IllegalStateException("Expected the warp-manager to be accessible at this point in time");
40 |
41 | platformScheduler.runAsync(task -> {
42 | var itemCount = updateAndInitiateNextUpdate();
43 | logger.info("Registered " + itemCount + " PlayerWarps!");
44 | });
45 | }
46 |
47 | private int updateAndInitiateNextUpdate() {
48 | clear();
49 |
50 | var warps = this.warpManager.getWarps();
51 |
52 | for (var playerWarp : warps)
53 | registerItem(playerWarp);
54 |
55 | int updatePeriod;
56 |
57 | if ((updatePeriod = config.rootSection.playerWarpsIntegration.updatePeriodSeconds) > 0)
58 | this.platformScheduler.runLaterAsync(this::updateAndInitiateNextUpdate, 20L * updatePeriod);
59 |
60 | return warps.size();
61 | }
62 |
63 | @Override
64 | public @Nullable PlayerWarpData locateNearestWithinRange(Player player, Location origin, int blockRadius) {
65 | var matchPredicate = IWorldGuardIntegration.makePredicate(origin, worldGuardIntegration, config.rootSection.playerWarpsIntegration.withinSameRegion);
66 | var nearestWarp = findClosestItem(origin, blockRadius, matchPredicate);
67 |
68 | if (nearestWarp == null)
69 | return null;
70 |
71 | var playerId = player.getUniqueId();
72 | var isBanned = false;
73 |
74 | for (var bannedId : nearestWarp.getBlockedPlayers()) {
75 | if (bannedId.equals(playerId)) {
76 | isBanned = true;
77 | break;
78 | }
79 | }
80 |
81 | return new PlayerWarpData(
82 | Bukkit.getOfflinePlayer(nearestWarp.getOwner()).getName(),
83 | nearestWarp.getName(),
84 | nearestWarp.getLocation(),
85 | isBanned
86 | );
87 | }
88 |
89 | @Override
90 | protected @Nullable Location extractItemLocation(Warp item) {
91 | return item.getLocation();
92 | }
93 |
94 | @Override
95 | protected boolean doItemsEqual(Warp a, Warp b) {
96 | return a.getWarpID().equals(b.getWarpID());
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/integration/worldguard_integration/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | me.blvckbytes
9 | quickshopsearch-root
10 | ${revision}
11 | ../../pom.xml
12 |
13 |
14 | worldguard_integration
15 |
16 |
17 | 17
18 | 17
19 | UTF-8
20 |
21 |
22 |
23 |
24 | sk89q-repo
25 | https://maven.enginehub.org/repo/
26 |
27 |
28 |
29 |
30 |
31 | com.sk89q.worldguard
32 | worldguard-bukkit
33 | 7.0.13
34 | provided
35 |
36 |
37 | me.blvckbytes
38 | common
39 | ${revision}
40 |
41 |
42 |
--------------------------------------------------------------------------------
/integration/worldguard_integration/src/main/java/me/blvckbytes/quick_shop_search/integration/worldguard/WorldGuardIntegration.java:
--------------------------------------------------------------------------------
1 | package me.blvckbytes.quick_shop_search.integration.worldguard;
2 |
3 | import com.sk89q.worldedit.bukkit.BukkitAdapter;
4 | import com.sk89q.worldedit.bukkit.BukkitWorld;
5 | import com.sk89q.worldguard.WorldGuard;
6 | import com.sk89q.worldguard.protection.regions.RegionContainer;
7 | import me.blvckbytes.bukkitevaluable.ConfigKeeper;
8 | import me.blvckbytes.quick_shop_search.config.MainSection;
9 | import org.bukkit.Location;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 | import java.util.logging.Logger;
14 |
15 | public class WorldGuardIntegration implements IWorldGuardIntegration {
16 |
17 | private final ConfigKeeper config;
18 | private final Logger logger;
19 | private final RegionContainer regionContainer;
20 |
21 | public WorldGuardIntegration(Logger logger, ConfigKeeper config) {
22 | this.logger = logger;
23 | this.config = config;
24 | this.regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer();
25 | }
26 |
27 | @Override
28 | public List getRegionsContainingLocation(Location location) {
29 | var locationWorld = location.getWorld();
30 |
31 | if (locationWorld == null)
32 | return List.of();
33 |
34 | var regionManager = regionContainer.get(new BukkitWorld(locationWorld));
35 |
36 | if (regionManager == null) {
37 | logger.warning("Could not access a RegionContainer for world " + locationWorld.getName());
38 | return List.of();
39 | }
40 |
41 | var position = BukkitAdapter.asBlockVector(location);
42 | var regions = regionManager.getApplicableRegions(position);
43 | var resultingIds = new ArrayList();
44 |
45 | for (var region : regions) {
46 | if (config.rootSection.worldGuardIntegration.ignoredIds.contains(region.getId()))
47 | continue;
48 |
49 | if (config.rootSection.worldGuardIntegration.ignoredPriorities.contains(region.getPriority()))
50 | continue;
51 |
52 | resultingIds.add(region.getId());
53 | }
54 |
55 | return resultingIds;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | me.blvckbytes
8 | quickshopsearch-root
9 | ${revision}
10 | pom
11 |
12 |
13 | 17
14 | 17
15 | UTF-8
16 | 0.0.47
17 |
18 |
19 |
20 | core
21 | common
22 | compatibility/qs_lte_6207
23 | compatibility/qs_gt_6207_lte_6208
24 | compatibility/qs_gt_6208
25 | integration/olziedev_player_warps_integration
26 | integration/revivalo_player_warps_integration
27 | integration/essentials_warps_integration
28 | integration/worldguard_integration
29 |
30 |
31 |
32 |
33 | codemc
34 | https://repo.codemc.io/repository/maven-public/
35 |
36 |
37 | spigot-repo
38 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/
39 |
40 |
41 |
42 |
43 |
44 | org.spigotmc
45 | spigot-api
46 | 1.18.2-R0.1-SNAPSHOT
47 | provided
48 |
49 |
50 | it.unimi.dsi
51 | fastutil
52 | 8.5.15
53 |
54 |
55 |
56 |
57 |
58 |
59 | ${basedir}/src/main/resources
60 | true
61 |
62 | plugin.yml
63 | config/**
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------