From 99aa349565e922e44fdca1adbb7a93ab752e31bf Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Thu, 3 Oct 2024 21:38:40 +0800 Subject: [PATCH] 3.6.14 --- .../customcrops/api/BukkitCustomCropsAPI.java | 137 ++++++++++++++++++ .../api/BukkitCustomCropsPlugin.java | 11 ++ .../customcrops/api/CustomCropsAPI.java | 74 ++++++++++ .../customcrops/api/core/block/CropBlock.java | 38 +++-- .../AbstractRequirementManager.java | 18 ++- .../item/MMOItemsItemProvider.java | 8 +- gradle.properties | 2 +- .../bukkit/BukkitCustomCropsPluginImpl.java | 1 + .../adaptor/BukkitWorldAdaptor.java | 2 +- .../bukkit/item/BukkitItemManager.java | 4 + .../bukkit/world/BukkitWorldManager.java | 12 +- 11 files changed, 268 insertions(+), 39 deletions(-) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsAPI.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/CustomCropsAPI.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsAPI.java b/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsAPI.java new file mode 100644 index 0000000..b78eea6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsAPI.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api; + +import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.FurnitureRotation; +import net.momirealms.customcrops.api.core.Registries; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Pos3; +import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent; +import net.momirealms.customcrops.api.util.DummyCancellable; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class BukkitCustomCropsAPI implements CustomCropsAPI { + + private static CustomCropsAPI instance; + + private final BukkitCustomCropsPlugin plugin; + + public BukkitCustomCropsAPI(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + instance = this; + } + + public static CustomCropsAPI get() { + return instance; + } + + @Override + public Pos3 adapt(Location location) { + return Pos3.from(location); + } + + @Override + public @Nullable CustomCropsWorld getCustomCropsWorld(String name) { + return plugin.getWorldManager().getWorld(name).orElse(null); + } + + @Override + public @Nullable CustomCropsWorld getCustomCropsWorld(World world) { + return plugin.getWorldManager().getWorld(world).orElse(null); + } + + @Override + public boolean placeCrop(Location location, String id, int point) { + CropConfig cropConfig = Registries.CROP.get(id); + if (cropConfig == null) { + return false; + } + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return false; + } + CustomCropsWorld world = optionalWorld.get(); + CropBlock cropBlock = (CropBlock) BuiltInBlockMechanics.CROP.mechanic(); + CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState(); + cropBlock.id(state, id); + cropBlock.point(state, point); + CropStageConfig stageConfigWithModel = cropConfig.stageWithModelByPoint(cropBlock.point(state)); + world.addBlockState(Pos3.from(location), state); + plugin.getScheduler().sync().run(() -> { + plugin.getItemManager().remove(location, ExistenceForm.ANY); + plugin.getItemManager().place(location, stageConfigWithModel.existenceForm(), requireNonNull(stageConfigWithModel.stageID()), cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE); + }, location); + return true; + } + + @Override + public void simulatePlayerBreakCrop( + Player player, + EquipmentSlot hand, + Location location, + BreakReason reason + ) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + Pos3 pos3 = Pos3.from(location); + CustomCropsWorld world = optionalWorld.get(); + Optional optionalState = world.getBlockState(pos3); + if (optionalState.isEmpty()) { + return; + } + CustomCropsBlockState state = optionalState.get(); + if (!(state.type() instanceof CropBlock cropBlock)) { + return; + } + CropConfig config = cropBlock.config(state); + if (config == null) { + return; + } + CropStageConfig stageConfig = config.stageWithModelByPoint(cropBlock.point(state)); + DummyCancellable dummyCancellable = new DummyCancellable(); + if (player != null) { + ItemStack itemStack = player.getInventory().getItem(hand); + state.type().onBreak(new WrappedBreakEvent(player, null, hand, location, stageConfig.stageID(), itemStack, plugin.getItemManager().id(itemStack), reason, world, dummyCancellable)); + } else { + state.type().onBreak(new WrappedBreakEvent(null, null, null, location, stageConfig.stageID(), null, null, reason, world, dummyCancellable)); + } + if (dummyCancellable.isCancelled()) { + return; + } + world.removeBlockState(pos3); + plugin.getItemManager().remove(location, ExistenceForm.ANY); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java index 7738882..08bb6dc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java +++ b/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java @@ -60,6 +60,7 @@ public abstract class BukkitCustomCropsPlugin implements CustomCropsPlugin { protected WorldManager worldManager; protected RegistryAccess registryAccess; protected SenderFactory senderFactory; + protected CustomCropsAPI api; protected final Map, ActionManager> actionManagers = new HashMap<>(); protected final Map, RequirementManager> requirementManagers = new HashMap<>(); @@ -74,6 +75,7 @@ public abstract class BukkitCustomCropsPlugin implements CustomCropsPlugin { throw new IllegalArgumentException("CustomCrops plugin requires custom crops plugin"); } this.bootstrap = bootstrap; + this.api = new BukkitCustomCropsAPI(this); instance = this; } @@ -200,6 +202,15 @@ public abstract class BukkitCustomCropsPlugin implements CustomCropsPlugin { return registryAccess; } + /** + * Get the API instance + * + * @return API + */ + public CustomCropsAPI getAPI() { + return api; + } + /** * Retrieves an ActionManager for a specific type. * diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsAPI.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsAPI.java new file mode 100644 index 0000000..5fa40fb --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsAPI.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api; + +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Pos3; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.jetbrains.annotations.Nullable; + +public interface CustomCropsAPI { + + /** + * Adapts a Bukkit Location to a Pos3 + * + * @param location location + * @return pos3 + */ + Pos3 adapt(Location location); + + /** + * Gets the CustomCrops world + * + * @param name world name + * @return the world + */ + @Nullable CustomCropsWorld getCustomCropsWorld(String name); + + /** + * Gets the CustomCrops world + * + * @param world Bukkit world + * @return the world + */ + @Nullable CustomCropsWorld getCustomCropsWorld(World world); + + /** + * Places a crop regardless of planting conditions such as pots. + * + * @param location location + * @param id crop id + * @param point point + * @return success or not + */ + boolean placeCrop(Location location, String id, int point); + + /** + * Performs actions to destroy crops on behalf of the player. + * + * @param player player + * @param hand hand + * @param location location + * @param reason reason + */ + void simulatePlayerBreakCrop(Player player, EquipmentSlot hand, Location location, BreakReason reason); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java index e557254..0e7f8fb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java @@ -50,7 +50,6 @@ import org.bukkit.inventory.ItemStack; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.CompletableFuture; public class CropBlock extends AbstractCustomCropsBlock { @@ -345,25 +344,7 @@ public class CropBlock extends AbstractCustomCropsBlock { World bukkitWorld = world.bukkitWorld(); Location bukkitLocation = location.toLocation(bukkitWorld); - CompletableFuture future = new CompletableFuture<>(); - if (ConfigManager.doubleCheck()) { - plugin.getScheduler().sync().run(() -> { - CropStageConfig nearest = config.stageWithModelByPoint(previousPoint); - String blockID = plugin.getItemManager().id(location.toLocation(bukkitWorld), nearest.existenceForm()); - if (!config.stageIDs().contains(blockID)) { - plugin.getPluginLogger().warn("Crop[" + config.id() + "] is removed at location[" + world.worldName() + "," + location + "] because the id of the block is [" + blockID + "]"); - world.removeBlockState(location); - future.complete(false); - return; - } - future.complete(true); - }, bukkitLocation); - } else { - future.complete(true); - } - - future.thenAcceptAsync((run) -> { - if (!run) return; + Runnable task = () -> { Context context = Context.block(state, bukkitLocation).arg(ContextKeys.OFFLINE, offline); for (DeathCondition deathCondition : config.deathConditions()) { if (deathCondition.isMet(context)) { @@ -430,7 +411,22 @@ public class CropBlock extends AbstractCustomCropsBlock { } } }, bukkitLocation); - }, world.scheduler().async()); + }; + + if (ConfigManager.doubleCheck()) { + plugin.getScheduler().sync().run(() -> { + CropStageConfig nearest = config.stageWithModelByPoint(previousPoint); + String blockID = plugin.getItemManager().id(location.toLocation(bukkitWorld), nearest.existenceForm()); + if (!config.stageIDs().contains(blockID)) { + plugin.getPluginLogger().warn("Crop[" + config.id() + "] is removed at location[" + world.worldName() + "," + location + "] because the id of the block is [" + blockID + "]"); + world.removeBlockState(location); + return; + } + world.scheduler().async().execute(task); + }, bukkitLocation); + } else { + task.run(); + } } public int point(CustomCropsBlockState state) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java b/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java index e50ffdf..b400dac 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java @@ -29,10 +29,7 @@ import net.momirealms.customcrops.api.core.block.ScarecrowBlock; import net.momirealms.customcrops.api.core.mechanic.crop.CrowAttack; import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; -import net.momirealms.customcrops.api.core.world.CustomCropsWorld; -import net.momirealms.customcrops.api.core.world.Pos3; -import net.momirealms.customcrops.api.core.world.Season; +import net.momirealms.customcrops.api.core.world.*; import net.momirealms.customcrops.api.misc.value.MathValue; import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.util.MoonPhase; @@ -988,10 +985,15 @@ public abstract class AbstractRequirementManager implements RequirementManage for (int i = -range; i <= range; i++) { for (int j = -range; j <= range; j++) { for (int k : new int[]{0,-1,1}) { - Optional optionalState = customCropsWorld.getBlockState(pos3.add(i, k, j)); - if (optionalState.isPresent() && optionalState.get().type() instanceof ScarecrowBlock) { - if (advanced) ActionManager.trigger(context, actions); - return false; + Pos3 tempPos3 = pos3.add(i, k, j); + Optional optionalChunk = customCropsWorld.getLoadedChunk(tempPos3.toChunkPos()); + if (optionalChunk.isPresent()) { + CustomCropsChunk chunk = optionalChunk.get(); + Optional optionalState = chunk.getBlockState(pos3); + if (optionalState.isPresent() && optionalState.get().type() instanceof ScarecrowBlock) { + if (advanced) ActionManager.trigger(context, actions); + return false; + } } } } diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MMOItemsItemProvider.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MMOItemsItemProvider.java index 2c8fb23..786183e 100644 --- a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MMOItemsItemProvider.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MMOItemsItemProvider.java @@ -41,7 +41,7 @@ public class MMOItemsItemProvider implements ItemProvider { @NotNull @Override public ItemStack buildItem(@NotNull Player player, @NotNull String id) { - String[] split = id.split(":"); + String[] split = id.split(":", 2); MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ENGLISH)); return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build()); } @@ -50,6 +50,10 @@ public class MMOItemsItemProvider implements ItemProvider { public String itemID(@NotNull ItemStack itemStack) { NBTItem nbtItem = NBTItem.get(itemStack); if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null; - return nbtItem.getString("MMOITEMS_ITEM_ID"); + if (nbtItem.hasType()) { + return nbtItem.getType() + ":" + nbtItem.getString("MMOITEMS_ITEM_ID"); + } else { + return nbtItem.getString("MMOITEMS_ITEM_ID"); + } } } diff --git a/gradle.properties b/gradle.properties index 346999f..a167981 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=3.6.13 +project_version=3.6.14 config_version=41 project_group=net.momirealms diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java index e5d82a4..41034e2 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -223,6 +223,7 @@ public class BukkitCustomCropsPluginImpl extends BukkitCustomCropsPlugin { this.coolDownManager.reload(); this.translationManager.reload(); this.hologramManager.reload(); + this.itemManager.reload(); this.actionManagers.values().forEach(Reloadable::reload); this.requirementManagers.values().forEach(Reloadable::reload); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java index 0086cc9..41c1e00 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java @@ -196,7 +196,7 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor { if (parentDir != null && !parentDir.exists()) { parentDir.mkdirs(); } - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) { + try (FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos)) { bos.write(serializeRegion(region)); long time2 = System.currentTimeMillis(); BukkitCustomCropsPlugin.getInstance().debug(() -> "[" + world.worldName() + "] Took " + (time2-time1) + "ms to save region " + region.regionPos()); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemManager.java index f0b2617..09d1a2f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemManager.java @@ -328,7 +328,9 @@ public class BukkitItemManager extends AbstractItemManager { if (itemStack == null || itemStack.getType() == Material.AIR) return "AIR"; String id = provider.itemID(itemStack); if (id != null) return id; + plugin.debug(() -> "Start checking ID from external plugins"); for (ItemProvider p : itemDetectArray) { + plugin.debug(p::identifier); id = p.itemID(itemStack); if (id != null) return p.identifier() + ":" + id; } @@ -458,6 +460,8 @@ public class BukkitItemManager extends AbstractItemManager { } private void handleInteractEvent(String blockID, WrappedInteractEvent wrapped) { + plugin.debug(() -> "Player [" + wrapped.player().getName() + "] interacted [" + blockID + "] with [" + wrapped.itemID() + "] at " + wrapped.location()); + CustomCropsItem customCropsItem = Registries.ITEMS.get(wrapped.itemID()); if (customCropsItem != null) { InteractionResult result = customCropsItem.interactAt(wrapped); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/world/BukkitWorldManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/world/BukkitWorldManager.java index 0349cb5..db51afd 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/world/BukkitWorldManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/world/BukkitWorldManager.java @@ -134,6 +134,12 @@ public class BukkitWorldManager implements WorldManager, Listener { } } + @Override + public void unload() { + HandlerList.unregisterAll(this); + this.worldSettings.clear(); + } + private void loadConfig() { YamlDocument config = BukkitConfigManager.getMainConfig(); @@ -170,12 +176,6 @@ public class BukkitWorldManager implements WorldManager, Listener { } } - @Override - public void unload() { - HandlerList.unregisterAll(this); - this.worldSettings.clear(); - } - @Override public void disable() { this.unload();