├── .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 | --------------------------------------------------------------------------------