From 3e7633a4b027ca665dde977a2946518458b78031 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Wed, 27 Mar 2024 02:09:53 +0800 Subject: [PATCH 001/329] fix convert bugs --- plugin/build.gradle.kts | 2 +- .../libraries/dependencies/Dependency.java | 2 +- .../mechanic/world/adaptor/BukkitWorldAdaptor.java | 14 +++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 08c6c4124..b446f45d6 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -56,7 +56,7 @@ dependencies { compileOnly("net.kyori:adventure-text-minimessage:4.15.0") compileOnly("net.kyori:adventure-text-serializer-legacy:4.15.0") - compileOnly("de.tr7zw:item-nbt-api:2.12.2") + compileOnly("de.tr7zw:item-nbt-api:2.12.3") compileOnly("org.bstats:bstats-bukkit:3.0.2") implementation("com.flowpowered:flow-nbt:2.0.2") implementation("com.github.luben:zstd-jni:1.5.5-11") diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 3618da982..7b9e28909 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -195,7 +195,7 @@ public enum Dependency { NBT_API( "de{}tr7zw", "item-nbt-api", - "2.12.2", + "2.12.3", "codemc", "item-nbt-api", Relocation.of("changeme", "de{}tr7zw{}changeme") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java index fc497cf67..ef5569dff 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java @@ -116,14 +116,16 @@ public void init(CustomCropsWorld customCropsWorld) { // try converting legacy worlds if (ConfigManager.convertWorldOnLoad()) { - convertWorldFromV33toV34(cWorld, world); - return; + if (convertWorldFromV33toV34(cWorld, world)) { + return; + } } if (VersionManager.isHigherThan1_18()) { // init world basic info String json = world.getPersistentDataContainer().get(key, PersistentDataType.STRING); WorldInfoData data = (json == null || json.equals("null")) ? WorldInfoData.empty() : gson.fromJson(json, WorldInfoData.class); + if (data == null) data = WorldInfoData.empty(); cWorld.setInfoData(data); } else { File cWorldFile = new File(getWorldFolder(world), "cworld.dat"); @@ -611,7 +613,7 @@ private byte[] serializeCompoundTag(CompoundTag tag) throws IOException { return outByteStream.toByteArray(); } - public void convertWorldFromV33toV34(@Nullable CWorld cWorld, World world) { + public boolean convertWorldFromV33toV34(@Nullable CWorld cWorld, World world) { // handle legacy files File leagcyFile = new File(world.getWorldFolder(), "customcrops" + File.separator + "data.yml"); if (leagcyFile.exists()) { @@ -633,10 +635,10 @@ public void convertWorldFromV33toV34(@Nullable CWorld cWorld, World world) { // read chunks File folder = new File(world.getWorldFolder(), "customcrops" + File.separator + "chunks"); - if (!folder.exists()) return; + if (!folder.exists()) return false; LogUtils.warn("Converting chunks for world " + world.getName() + " from 3.3 to 3.4... This might take some time."); File[] data_files = folder.listFiles(); - if (data_files == null) return; + if (data_files == null) return false; HashMap regionHashMap = new HashMap<>(); @@ -685,7 +687,9 @@ public void convertWorldFromV33toV34(@Nullable CWorld cWorld, World world) { saveRegion(region); } LogUtils.info("Successfully converted chunks for world: " + world.getName()); + return true; } + return false; } public void convertWorldFromV342toV343(@Nullable CWorld cWorld, World world) { From c82244188e14e7f25d5692378eb2f2e9ee4712c6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Fri, 29 Mar 2024 13:09:20 +0800 Subject: [PATCH 002/329] fix item detection --- .../api/mechanic/requirement/State.java | 3 ++- .../customcrops/api/util/LocationUtils.java | 19 +++++++++++++++++++ build.gradle.kts | 2 +- .../compatibility/item/MMOItemsItemImpl.java | 2 +- .../mechanic/item/CustomProvider.java | 7 ++++--- .../mechanic/item/ItemManagerImpl.java | 13 +++++++------ .../custom/crucible/CrucibleProvider.java | 6 +----- .../custom/itemsadder/ItemsAdderProvider.java | 5 ++--- .../item/custom/oraxen/OraxenListener.java | 5 +++-- .../item/custom/oraxen/OraxenProvider.java | 8 +------- .../customcrops/mechanic/world/CWorld.java | 15 +++++++++++---- plugin/src/main/resources/plugin.yml | 2 +- 12 files changed, 53 insertions(+), 34 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java index 73d9836fc..630a49a0f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.mechanic.requirement; +import net.momirealms.customcrops.api.util.LocationUtils; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -35,7 +36,7 @@ public class State { public State(Player player, ItemStack itemInHand, @NotNull Location location) { this.player = player; this.itemInHand = itemInHand; - this.location = location.toBlockLocation(); + this.location = LocationUtils.toBlockLocation(location); this.args = new HashMap<>(); if (player != null) { setArg("{player}", player.getName()); diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java index e865ce04e..41c287929 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java @@ -19,6 +19,7 @@ import org.bukkit.Bukkit; import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; public class LocationUtils { @@ -41,4 +42,22 @@ public static double getDistance(Location location1, Location location2) { public static Location getAnyLocationInstance() { return new Location(Bukkit.getWorlds().get(0), 0, 64, 0); } + + @NotNull + public static Location toBlockLocation(Location location) { + Location blockLoc = location.clone(); + blockLoc.setX(location.getBlockX()); + blockLoc.setY(location.getBlockY()); + blockLoc.setZ(location.getBlockZ()); + return blockLoc; + } + + @NotNull + public static Location toCenterLocation(Location location) { + Location centerLoc = location.clone(); + centerLoc.setX(location.getBlockX() + 0.5); + centerLoc.setY(location.getBlockY() + 0.5); + centerLoc.setZ(location.getBlockZ() + 0.5); + return centerLoc; + } } diff --git a/build.gradle.kts b/build.gradle.kts index 047e8fa78..62352d7ae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.3.3" + project.version = "3.4.3.4" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java index 6d8ad2511..4d9578f93 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java @@ -46,6 +46,6 @@ public ItemStack buildItem(Player player, String id) { public String getItemID(ItemStack itemStack) { NBTItem nbtItem = new NBTItem(itemStack); if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null; - return nbtItem.getString("MMOITEMS_ITEM_ID"); + return nbtItem.getString("MMOITEMS_ITEM_TYPE") + ":" + nbtItem.getString("MMOITEMS_ITEM_ID"); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java index c23dd29c2..1f6b77215 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java @@ -19,6 +19,7 @@ import net.momirealms.customcrops.api.manager.VersionManager; import net.momirealms.customcrops.api.mechanic.misc.CRotation; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.utils.ConfigUtils; import net.momirealms.customcrops.utils.DisplayEntityUtils; import net.momirealms.customcrops.utils.RotationUtils; @@ -65,7 +66,7 @@ default boolean isAir(Location location) { Block block = location.getBlock(); if (block.getType() != Material.AIR) return false; - Location center = location.toCenterLocation(); + Location center = LocationUtils.toCenterLocation(location); Collection entities = center.getWorld().getNearbyEntities(center, 0.5,0.51,0.5); entities.removeIf(entity -> entity instanceof Player); return entities.size() == 0; @@ -73,7 +74,7 @@ default boolean isAir(Location location) { default CRotation removeAnythingAt(Location location) { if (!removeBlock(location)) { - Collection entities = location.getWorld().getNearbyEntities(location.toCenterLocation(), 0.5,0.51,0.5); + Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toCenterLocation(location), 0.5,0.51,0.5); entities.removeIf(entity -> { EntityType type = entity.getType(); return type != EntityType.ITEM_FRAME @@ -102,7 +103,7 @@ default String getSomethingAt(Location location) { if (block.getType() != Material.AIR) { return getBlockID(block); } else { - Collection entities = location.getWorld().getNearbyEntities(location.toCenterLocation(), 0.5,0.5,0.5); + Collection entities = location.getWorld().getNearbyEntities(location.toCenterLocation(), 0.5,0.51,0.5); for (Entity entity : entities) { if (isFurniture(entity)) { return getEntityID(entity); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 5f7f147c2..2498be44f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -38,6 +38,7 @@ import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.*; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; import net.momirealms.customcrops.mechanic.item.custom.crucible.CrucibleListener; @@ -124,13 +125,13 @@ public ItemManagerImpl(CustomCropsPlugin plugin, AntiGriefLib antiGriefLib) { this.item2FertilizerMap = new HashMap<>(); this.stage2CropStageMap = new HashMap<>(); this.deadCrops = new HashSet<>(); - if (Bukkit.getPluginManager().isPluginEnabled("Oraxen")) { + if (Bukkit.getPluginManager().getPlugin("Oraxen") != null) { listener = new OraxenListener(this); customProvider = new OraxenProvider(); - } else if (Bukkit.getPluginManager().isPluginEnabled("ItemsAdder")) { + } else if (Bukkit.getPluginManager().getPlugin("ItemsAdder") != null) { listener = new ItemsAdderListener(this); customProvider = new ItemsAdderProvider(); - } else if (Bukkit.getPluginManager().isPluginEnabled("MythicCrucible")) { + } else if (Bukkit.getPluginManager().getPlugin("MythicCrucible") != null) { listener = new CrucibleListener(this); customProvider = new CrucibleProvider(); } else { @@ -221,7 +222,7 @@ public String getItemID(ItemStack itemStack) { for (ItemLibrary library : itemDetectionArray) { id = library.getItemID(itemStack); if (id != null) - return id; + return library.identification() + ":" + id; } } return itemStack.getType().name(); @@ -1821,7 +1822,7 @@ private void loadCrop(String key, ConfigurationSection section) { } Player player = interactWrapper.getPlayer(); - Location cropLocation = interactWrapper.getLocation().toBlockLocation(); + Location cropLocation = LocationUtils.toBlockLocation(interactWrapper.getLocation()); ItemStack itemInHand = interactWrapper.getItemInHand(); State cropState = new State(player, itemInHand, cropLocation); @@ -1928,7 +1929,7 @@ private void loadCrop(String key, ConfigurationSection section) { return FunctionResult.PASS; } Player player = breakWrapper.getPlayer(); - Location cropLocation = breakWrapper.getLocation().toBlockLocation(); + Location cropLocation = LocationUtils.toBlockLocation(breakWrapper.getLocation()); State state = new State(player, breakWrapper.getItemInHand(), cropLocation); // check crop break requirements if (!RequirementManager.isRequirementMet(state, crop.getBreakRequirements())) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java index 705e499f3..aa9a73ff2 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java @@ -76,8 +76,6 @@ public void placeCustomBlock(Location location, String id) { @Override public Entity placeFurniture(Location location, String id) { - Location center = location.toCenterLocation(); - center.setY(center.getBlockY()); Optional optionalCI = itemManager.getItem(id); if (optionalCI.isPresent()) { return optionalCI.get().getFurnitureData().placeFrame(location.getBlock(), BlockFace.UP, 0f, null); @@ -101,9 +99,7 @@ public String getBlockID(Block block) { @Override public String getItemID(ItemStack itemStack) { - Optional optionalCI = itemManager.getItem(itemStack); - if (optionalCI.isEmpty()) return itemStack.getType().name(); - else return optionalCI.get().getInternalName(); + return itemManager.getItem(itemStack).map(CrucibleItem::getInternalName).orElse(null); } @Override diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java index d008c9939..7604f3d79 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java @@ -20,6 +20,7 @@ import dev.lone.itemsadder.api.CustomBlock; import dev.lone.itemsadder.api.CustomFurniture; import dev.lone.itemsadder.api.CustomStack; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.mechanic.item.CustomProvider; import org.bukkit.Location; @@ -52,8 +53,6 @@ public void placeCustomBlock(Location location, String id) { @Override public Entity placeFurniture(Location location, String id) { try { - Location center = location.toCenterLocation(); - center.setY(center.getBlockY()); CustomFurniture furniture = CustomFurniture.spawnPreciseNonSolid(id, location); if (furniture == null) return null; return furniture.getEntity(); @@ -82,7 +81,7 @@ public String getBlockID(Block block) { public String getItemID(ItemStack itemInHand) { CustomStack customStack = CustomStack.byItemStack(itemInHand); if (customStack == null) { - return itemInHand.getType().name(); + return null; } return customStack.getNamespacedID(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java index 8991f0a73..41bc62e15 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java @@ -24,6 +24,7 @@ import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockPlaceEvent; import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockBreakEvent; import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockPlaceEvent; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; import org.bukkit.event.EventHandler; @@ -88,7 +89,7 @@ public void onPlaceFurniture(OraxenFurniturePlaceEvent event) { public void onBreakFurniture(OraxenFurnitureBreakEvent event) { super.onBreakFurniture( event.getPlayer(), - event.getBaseEntity().getLocation().toBlockLocation(), + LocationUtils.toBlockLocation(event.getBaseEntity().getLocation()), event.getMechanic().getItemID(), event ); @@ -98,7 +99,7 @@ public void onBreakFurniture(OraxenFurnitureBreakEvent event) { public void onInteractFurniture(OraxenFurnitureInteractEvent event) { super.onInteractFurniture( event.getPlayer(), - event.getBaseEntity().getLocation().toBlockLocation(), + LocationUtils.toBlockLocation(event.getBaseEntity().getLocation()), event.getMechanic().getItemID(), event.getBaseEntity(), event diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java index 1b2d5bd14..754cd9b3c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java @@ -54,8 +54,6 @@ public void placeCustomBlock(Location location, String id) { @Override public Entity placeFurniture(Location location, String id) { - Location center = location.toCenterLocation(); - center.setY(center.getBlockY()); Entity entity = OraxenFurniture.place(id, location, Rotation.NONE, BlockFace.UP); if (entity == null) { LogUtils.warn("Furniture(" + id +") doesn't exist in Oraxen configs. Please double check if that furniture exists."); @@ -79,11 +77,7 @@ public String getBlockID(Block block) { @Override public String getItemID(ItemStack itemStack) { - String id = OraxenItems.getIdByItem(itemStack); - if (id == null) { - return itemStack.getType().name(); - } - return id; + return OraxenItems.getIdByItem(itemStack); } @Override diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index 6ce3304d1..052024d6d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -37,6 +37,7 @@ import net.momirealms.customcrops.api.scheduler.Scheduler; import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.utils.EventUtils; +import org.bukkit.Bukkit; import org.bukkit.World; import org.jetbrains.annotations.Nullable; @@ -51,7 +52,7 @@ public class CWorld implements CustomCropsWorld { private final WorldManager worldManager; - private final WeakReference world; + private WeakReference world; private final ConcurrentHashMap loadedChunks; private final ConcurrentHashMap lazyChunks; private final ConcurrentHashMap loadedRegions; @@ -124,7 +125,7 @@ private void timer() { if (VersionManager.folia()) { Scheduler scheduler = CustomCropsPlugin.get().getScheduler(); for (CChunk chunk : loadedChunks.values()) { - scheduler.runTaskSync(chunk::secondTimer,world.get(), chunk.getChunkPos().x(), chunk.getChunkPos().z()); + scheduler.runTaskSync(chunk::secondTimer, getWorld(), chunk.getChunkPos().x(), chunk.getChunkPos().z()); } } else { for (CChunk chunk : loadedChunks.values()) { @@ -151,7 +152,7 @@ private void timer() { } private void updateSeasonAndDate() { - World bukkitWorld = world.get(); + World bukkitWorld = getWorld(); if (bukkitWorld == null) { LogUtils.severe(String.format("World %s unloaded unexpectedly. Stop ticking task...", worldName)); this.cancelTick(); @@ -201,7 +202,13 @@ public Collection getChunkStorage() { @Nullable @Override public World getWorld() { - return world.get(); + return Optional.ofNullable(world.get()).orElseGet(() -> { + World bukkitWorld = Bukkit.getWorld(worldName); + if (bukkitWorld != null) { + this.world = new WeakReference<>(bukkitWorld); + } + return bukkitWorld; + }); } @Override diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 1bf4774cc..24cfed31f 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -34,7 +34,7 @@ softdepend: - GriefDefender - GriefPrevention - BentoBox - - IridiumSkyBlock + - IridiumSkyblock - KingdomsX - Landlord - Lands From 504c8be2687c2035df876990cb3ad917d1768ade Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Fri, 29 Mar 2024 13:36:09 +0800 Subject: [PATCH 003/329] switch to itemManager's getItemID --- .../customcrops/mechanic/item/ItemManagerImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 2498be44f..97e00859d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -2268,7 +2268,7 @@ public void handlePlayerInteractBlock( return; } // Then check item in hand - String itemID = customProvider.getItemID(condition.getItemInHand()); + String itemID = getItemID(condition.getItemInHand()); Optional.ofNullable(itemID2FunctionMap.get(itemID)) .map(map -> map.get(FunctionTrigger.INTERACT_AT)) .ifPresent(itemFunctions -> handleFunctions(itemFunctions, condition, event)); @@ -2287,7 +2287,7 @@ public void handlePlayerInteractAir( var condition = new InteractWrapper(player, null); // check item in hand - String itemID = customProvider.getItemID(condition.getItemInHand()); + String itemID = getItemID(condition.getItemInHand()); Optional.ofNullable(itemID2FunctionMap.get(itemID)) .map(map -> map.get(FunctionTrigger.INTERACT_AIR)) .ifPresent(cFunctions -> handleFunctions(cFunctions, condition, event)); @@ -2333,7 +2333,7 @@ public void handlePlayerInteractFurniture( return; } // Then check item in hand - String itemID = customProvider.getItemID(condition.getItemInHand()); + String itemID = getItemID(condition.getItemInHand()); Optional.ofNullable(itemID2FunctionMap.get(itemID)) .map(map -> map.get(FunctionTrigger.INTERACT_AT)) .ifPresent(cFunctions -> handleFunctions(cFunctions, condition, event)); From 3bde4340b26c4fb86e731c9d221e0a0f2f57c024 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 30 Mar 2024 15:59:30 +0800 Subject: [PATCH 004/329] update default configs --- .../momirealms/customcrops/manager/AdventureManagerImpl.java | 2 +- plugin/src/main/resources/config.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java index 64552f951..6ba95108c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java @@ -163,7 +163,7 @@ public String legacyToMiniMessage(String legacy) { case 'm' -> stringBuilder.append(""); case 'o' -> stringBuilder.append(""); case 'n' -> stringBuilder.append(""); - case 'k' -> stringBuilder.append(""); + case 'k' -> stringBuilder.append(""); case 'x' -> { if (i + 13 >= chars.length || !isColorCode(chars[i+2]) diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 3b538ba86..fc0669e73 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -101,7 +101,7 @@ mechanics: # Sync seasons sync-season: - enable: true + enable: false reference: world # Vanilla farmland settings From 024cb479d02972b81d2a4345fce862349a740c51 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Wed, 3 Apr 2024 18:53:51 +0800 Subject: [PATCH 005/329] untested offline growth --- .../api/manager/ConditionManager.java | 4 +- .../api/mechanic/condition/Condition.java | 2 +- .../api/mechanic/world/SimpleLocation.java | 2 +- .../api/mechanic/world/Tickable.java | 2 +- .../mechanic/world/level/WorldSetting.java | 27 +++-- build.gradle.kts | 2 +- plugin/build.gradle.kts | 2 +- .../customcrops/CustomCropsPluginImpl.java | 2 +- .../customcrops/manager/CommandManager.java | 2 +- .../manager/ConfigManagerImpl.java | 2 +- .../customcrops/manager/HologramManager.java | 2 +- .../manager/MessageManagerImpl.java | 2 +- .../manager/PlaceholderManagerImpl.java | 2 +- .../mechanic/action/ActionManagerImpl.java | 12 +- .../condition/ConditionManagerImpl.java | 108 +++++++++++------- .../mechanic/condition/EmptyCondition.java | 2 +- .../mechanic/item/CustomProvider.java | 6 +- .../mechanic/item/ItemManagerImpl.java | 10 +- .../item/custom/AbstractCustomListener.java | 2 +- .../custom/itemsadder/ItemsAdderProvider.java | 1 - .../item/custom/oraxen/OraxenProvider.java | 4 +- .../mechanic/item/impl/PotConfig.java | 2 +- .../mechanic/misc/CrowAttackAnimation.java | 2 +- .../mechanic/misc/TempFakeItem.java | 2 +- .../mechanic/misc/migrator/Migration.java | 6 +- .../mechanic/misc/value/ExpressionValue.java | 2 +- .../requirement/RequirementManagerImpl.java | 4 +- .../customcrops/mechanic/world/CChunk.java | 42 +++++-- .../customcrops/mechanic/world/CWorld.java | 66 ++++++----- .../mechanic/world/WorldManagerImpl.java | 3 +- .../mechanic/world/block/MemoryCrop.java | 12 +- .../mechanic/world/block/MemoryGlass.java | 2 +- .../mechanic/world/block/MemoryPot.java | 3 +- .../mechanic/world/block/MemoryScarecrow.java | 2 +- .../mechanic/world/block/MemorySprinkler.java | 37 ++++-- .../{utils => util}/ClassUtils.java | 2 +- .../{utils => util}/ConfigUtils.java | 5 +- .../{utils => util}/DisplayEntityUtils.java | 2 +- .../{utils => util}/EventUtils.java | 2 +- .../{utils => util}/FakeEntityUtils.java | 2 +- .../{utils => util}/ItemUtils.java | 2 +- .../{utils => util}/RotationUtils.java | 2 +- plugin/src/main/resources/config.yml | 10 ++ 43 files changed, 257 insertions(+), 153 deletions(-) rename plugin/src/main/java/net/momirealms/customcrops/{utils => util}/ClassUtils.java (98%) rename plugin/src/main/java/net/momirealms/customcrops/{utils => util}/ConfigUtils.java (98%) rename plugin/src/main/java/net/momirealms/customcrops/{utils => util}/DisplayEntityUtils.java (90%) rename plugin/src/main/java/net/momirealms/customcrops/{utils => util}/EventUtils.java (96%) rename plugin/src/main/java/net/momirealms/customcrops/{utils => util}/FakeEntityUtils.java (99%) rename plugin/src/main/java/net/momirealms/customcrops/{utils => util}/ItemUtils.java (99%) rename plugin/src/main/java/net/momirealms/customcrops/{utils => util}/RotationUtils.java (98%) diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java index adf3358c6..59c0b70bb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java @@ -42,10 +42,10 @@ public interface ConditionManager extends Reloadable { @Nullable ConditionFactory getConditionFactory(String type); - static boolean isConditionMet(CustomCropsBlock block, Condition... conditions) { + static boolean isConditionMet(CustomCropsBlock block, boolean offline, Condition... conditions) { if (conditions == null) return true; for (Condition condition : conditions) { - if (!condition.isConditionMet(block)) { + if (!condition.isConditionMet(block, offline)) { return false; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java index 8b7f1e8b4..fc9a96f38 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java @@ -21,5 +21,5 @@ public interface Condition { - boolean isConditionMet(CustomCropsBlock block); + boolean isConditionMet(CustomCropsBlock block, boolean offline); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java index 403dfb754..f7765a03b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java @@ -57,7 +57,7 @@ public String getWorldName() { return worldName; } - public ChunkPos getChunkCoordinate() { + public ChunkPos getChunkPos() { return new ChunkPos(x >> 4, z >> 4); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java index 047517ba6..db9576650 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java @@ -2,5 +2,5 @@ public interface Tickable { - void tick(int interval); + void tick(int interval, boolean offline); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java index 0ca961451..da4b26809 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java @@ -32,6 +32,7 @@ public class WorldSetting implements Cloneable { private final int potPerChunk; private final int sprinklerPerChunk; private final int randomTickSpeed; + private final int maxOfflineTime; private final boolean tickCropRandomly; private final boolean tickPotRandomly; private final boolean tickSprinklerRandomly; @@ -47,6 +48,7 @@ private WorldSetting( boolean tickSprinklerRandomly, int tickSprinklerInterval, boolean offlineGrow, + int maxOfflineTime, boolean enableSeason, boolean autoSeasonChange, int seasonDuration, @@ -61,6 +63,7 @@ private WorldSetting( this.tickPotInterval = tickPotInterval; this.tickSprinklerInterval = tickSprinklerInterval; this.offlineGrow = offlineGrow; + this.maxOfflineTime = maxOfflineTime; this.enableSeason = enableSeason; this.autoSeasonChange = autoSeasonChange; this.seasonDuration = seasonDuration; @@ -84,6 +87,7 @@ public static WorldSetting of( boolean tickSprinklerRandomly, int tickSprinklerInterval, boolean offlineGrow, + int maxOfflineTime, boolean enableSeason, boolean autoSeasonChange, int seasonDuration, @@ -102,6 +106,7 @@ public static WorldSetting of( tickSprinklerRandomly, tickSprinklerInterval, offlineGrow, + maxOfflineTime, enableSeason, autoSeasonChange, seasonDuration, @@ -168,15 +173,6 @@ public boolean isScheduledTick() { return scheduledTick; } - @Override - public WorldSetting clone() { - try { - return (WorldSetting) super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } - public boolean randomTickCrop() { return tickCropRandomly; } @@ -188,4 +184,17 @@ public boolean randomTickSprinkler() { public boolean randomTickPot() { return tickPotRandomly; } + + public int getMaxOfflineTime() { + return maxOfflineTime; + } + + @Override + public WorldSetting clone() { + try { + return (WorldSetting) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } } diff --git a/build.gradle.kts b/build.gradle.kts index 62352d7ae..6573a7da7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.3.4" + project.version = "3.4.4" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index b446f45d6..d5ff9f571 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { // Items compileOnly("com.github.LoneDev6:api-itemsadder:3.6.2-beta-r3-b") - compileOnly("com.github.oraxen:oraxen:1.168.0") + compileOnly("com.github.oraxen:oraxen:1.172.0") compileOnly("pers.neige.neigeitems:NeigeItems:1.16.24") compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT") compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT") diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index 917ad0e6a..4953c70c1 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -38,7 +38,7 @@ import net.momirealms.customcrops.mechanic.requirement.RequirementManagerImpl; import net.momirealms.customcrops.mechanic.world.WorldManagerImpl; import net.momirealms.customcrops.scheduler.SchedulerImpl; -import net.momirealms.customcrops.utils.EventUtils; +import net.momirealms.customcrops.util.EventUtils; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java index 7b2aa1fcd..67a71dd9f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java @@ -113,7 +113,7 @@ private CommandAPICommand getForceTickCommand() { for (CustomCropsSection section : chunk.getSections()) { for (CustomCropsBlock block : section.getBlocks()) { if (block.getType() == itemType) { - block.tick(1); + block.tick(1, false); } } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java index f1e6c4299..88ee65a8b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java @@ -26,7 +26,7 @@ import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.manager.ConfigManager; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java index e5a4d1a7c..154f77fd4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java @@ -23,7 +23,7 @@ import net.momirealms.customcrops.api.common.Tuple; import net.momirealms.customcrops.api.manager.VersionManager; import net.momirealms.customcrops.api.scheduler.CancellableTask; -import net.momirealms.customcrops.utils.FakeEntityUtils; +import net.momirealms.customcrops.util.FakeEntityUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.EntityType; diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java index ce26b8a86..a273b2202 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java @@ -22,7 +22,7 @@ import net.momirealms.customcrops.api.manager.ConfigManager; import net.momirealms.customcrops.api.manager.MessageManager; import net.momirealms.customcrops.api.mechanic.world.season.Season; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java index 13da54ce0..0e36df4ce 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java @@ -21,7 +21,7 @@ import net.momirealms.customcrops.api.manager.PlaceholderManager; import net.momirealms.customcrops.compatibility.papi.CCPapi; import net.momirealms.customcrops.compatibility.papi.ParseUtils; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.Bukkit; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index e97f1414a..b3f4501d7 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -56,10 +56,10 @@ import net.momirealms.customcrops.mechanic.item.impl.VariationCrop; import net.momirealms.customcrops.mechanic.misc.TempFakeItem; import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; -import net.momirealms.customcrops.utils.ClassUtils; -import net.momirealms.customcrops.utils.ConfigUtils; -import net.momirealms.customcrops.utils.EventUtils; -import net.momirealms.customcrops.utils.ItemUtils; +import net.momirealms.customcrops.util.ClassUtils; +import net.momirealms.customcrops.util.ConfigUtils; +import net.momirealms.customcrops.util.EventUtils; +import net.momirealms.customcrops.util.ItemUtils; import org.bukkit.*; import org.bukkit.block.BlockFace; import org.bukkit.configuration.ConfigurationSection; @@ -220,6 +220,7 @@ private void registerHologramAction() { boolean onlyShowToOne = !section.getBoolean("visible-to-all", false); return condition -> { if (Math.random() > chance) return; + if (condition.getArg("{offline}") != null) return; Location location = condition.getLocation().clone().add(x,y,z); SimpleLocation simpleLocation = SimpleLocation.of(location); if (applyCorrection) { @@ -269,6 +270,7 @@ private void registerFakeItemAction() { boolean onlyShowToOne = !section.getBoolean("visible-to-all", true); return condition -> { if (Math.random() > chance) return; + if (condition.getArg("{offline}") != null) return; if (item.equals("")) return; Location location = condition.getLocation().clone().add(x,y,z); new TempFakeItem(location, item, duration, onlyShowToOne ? condition.getPlayer() : null).start(); @@ -446,7 +448,7 @@ private void registerForceTickAction() { .flatMap(world -> world.getLoadedChunkAt(ChunkPos.getByBukkitChunk(location.getChunk()))) .flatMap(chunk -> chunk.getBlockAt(SimpleLocation.of(location))) .ifPresent(block -> { - block.tick(1); + block.tick(1, false); if (block instanceof WorldSprinkler sprinkler) { Sprinkler config = sprinkler.getConfig(); state.setArg("{current}", String.valueOf(sprinkler.getWater())); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java index 6ffdb02f6..19356a7ef 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java @@ -33,8 +33,9 @@ import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.compatibility.papi.ParseUtils; import net.momirealms.customcrops.mechanic.misc.CrowAttackAnimation; -import net.momirealms.customcrops.utils.ClassUtils; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; +import net.momirealms.customcrops.util.ClassUtils; +import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.data.type.Farmland; @@ -95,6 +96,7 @@ private void registerInbuiltConditions() { this.registerCrowAttackCondition(); this.registerPotCondition(); this.registerLightCondition(); + this.registerPointCondition(); } @Override @@ -167,7 +169,7 @@ private void registerCrowAttackCondition() { String flyModel = section.getString("fly-model"); String standModel = section.getString("stand-model"); double chance = section.getDouble("chance"); - return block -> { + return (block, offline) -> { if (Math.random() > chance) return false; SimpleLocation location = block.getLocation(); if (ConfigManager.enableScarecrow()) { @@ -185,7 +187,8 @@ private void registerCrowAttackCondition() { } } } - new CrowAttackAnimation(location, flyModel, standModel).start(); + if (!offline) + new CrowAttackAnimation(location, flyModel, standModel).start(); return true; }; } else { @@ -198,14 +201,14 @@ private void registerCrowAttackCondition() { private void registerBiomeRequirement() { registerCondition("biome", (args) -> { HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return block -> { + return (block, offline) -> { String currentBiome = BiomeAPI.getBiomeAt(block.getLocation().getBukkitLocation()); return biomes.contains(currentBiome); }; }); registerCondition("!biome", (args) -> { HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return block -> { + return (block, offline) -> { String currentBiome = BiomeAPI.getBiomeAt(block.getLocation().getBukkitLocation()); return !biomes.contains(currentBiome); }; @@ -215,21 +218,21 @@ private void registerBiomeRequirement() { private void registerRandomCondition() { registerCondition("random", (args -> { double value = ConfigUtils.getDoubleValue(args); - return block -> Math.random() < value; + return (block, offline) -> Math.random() < value; })); } private void registerPotCondition() { registerCondition("pot", (args -> { HashSet pots = new HashSet<>(ConfigUtils.stringListArgs(args)); - return block -> { + return (block, offline) -> { Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); return worldPot.filter(pot -> pots.contains(pot.getKey())).isPresent(); }; })); registerCondition("!pot", (args -> { HashSet pots = new HashSet<>(ConfigUtils.stringListArgs(args)); - return block -> { + return (block, offline) -> { Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); return worldPot.filter(pot -> !pots.contains(pot.getKey())).isPresent(); }; @@ -239,7 +242,7 @@ private void registerPotCondition() { private void registerFertilizerCondition() { registerCondition("fertilizer", (args -> { HashSet fertilizer = new HashSet<>(ConfigUtils.stringListArgs(args)); - return block -> { + return (block, offline) -> { Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); return worldPot.filter(pot -> { Fertilizer fertilizerInstance = pot.getFertilizer(); @@ -250,7 +253,7 @@ private void registerFertilizerCondition() { })); registerCondition("fertilizer_type", (args -> { HashSet fertilizer = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(str -> str.toUpperCase(Locale.ENGLISH)).toList()); - return block -> { + return (block, offline) -> { Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); return worldPot.filter(pot -> { Fertilizer fertilizerInstance = pot.getFertilizer(); @@ -265,7 +268,7 @@ private void registerAndCondition() { registerCondition("&&", (args -> { if (args instanceof ConfigurationSection section) { Condition[] conditions = getConditions(section); - return block -> ConditionManager.isConditionMet(block, conditions); + return (block, offline) -> ConditionManager.isConditionMet(block, offline, conditions); } else { LogUtils.warn("Wrong value format found at && condition."); return EmptyCondition.instance; @@ -277,9 +280,9 @@ private void registerOrCondition() { registerCondition("||", (args -> { if (args instanceof ConfigurationSection section) { Condition[] conditions = getConditions(section); - return block -> { + return (block, offline) -> { for (Condition condition : conditions) { - if (condition.isConditionMet(block)) { + if (condition.isConditionMet(block, offline)) { return true; } } @@ -295,7 +298,7 @@ private void registerOrCondition() { private void registerTemperatureCondition() { registerCondition("temperature", (args) -> { List> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return block -> { + return (block, offline) -> { SimpleLocation location = block.getLocation(); World world = location.getBukkitWorld(); if (world == null) return false; @@ -316,7 +319,7 @@ private void registerGreaterThanCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return Double.parseDouble(p1) >= Double.parseDouble(p2); @@ -330,7 +333,7 @@ private void registerGreaterThanCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return Double.parseDouble(p1) > Double.parseDouble(p2); @@ -347,7 +350,7 @@ private void registerRegexCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("papi", ""); String v2 = section.getString("regex", ""); - return block -> ParseUtils.setPlaceholders(null, v1).matches(v2); + return (block, offline) -> ParseUtils.setPlaceholders(null, v1).matches(v2); } else { LogUtils.warn("Wrong value format found at regex requirement."); return EmptyCondition.instance; @@ -360,7 +363,7 @@ private void registerNumberEqualCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return Double.parseDouble(p1) == Double.parseDouble(p2); @@ -374,7 +377,7 @@ private void registerNumberEqualCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return Double.parseDouble(p1) != Double.parseDouble(p2); @@ -392,7 +395,7 @@ private void registerLessThanCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return Double.parseDouble(p1) < Double.parseDouble(p2); @@ -406,7 +409,7 @@ private void registerLessThanCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return Double.parseDouble(p1) <= Double.parseDouble(p2); @@ -423,7 +426,7 @@ private void registerStartWithCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return p1.startsWith(p2); @@ -437,7 +440,7 @@ private void registerStartWithCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return !p1.startsWith(p2); @@ -454,7 +457,7 @@ private void registerEndWithCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return p1.endsWith(p2); @@ -468,7 +471,7 @@ private void registerEndWithCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return !p1.endsWith(p2); @@ -485,7 +488,7 @@ private void registerContainCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return p1.contains(p2); @@ -499,7 +502,7 @@ private void registerContainCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return !p1.contains(p2); @@ -516,7 +519,7 @@ private void registerInListCondition() { if (args instanceof ConfigurationSection section) { String papi = section.getString("papi", ""); HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); - return block -> { + return (block, offline) -> { String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(null, papi) : papi; return values.contains(p1); }; @@ -529,7 +532,7 @@ private void registerInListCondition() { if (args instanceof ConfigurationSection section) { String papi = section.getString("papi", ""); HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); - return block -> { + return (block, offline) -> { String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(null, papi) : papi; return !values.contains(p1); }; @@ -545,7 +548,7 @@ private void registerEqualsCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return p1.equals(p2); @@ -559,7 +562,7 @@ private void registerEqualsCondition() { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); String v2 = section.getString("value2", ""); - return block -> { + return (block, offline) -> { String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; return !p1.equals(p2); @@ -574,7 +577,7 @@ private void registerEqualsCondition() { private void registerSeasonCondition() { registerCondition("suitable_season", (args) -> { HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); - return block -> { + return (block, offline) -> { Season season = plugin.getIntegrationManager().getSeason(block.getLocation().getBukkitWorld()); if (season == null) { return true; @@ -598,7 +601,7 @@ private void registerSeasonCondition() { }); registerCondition("unsuitable_season", (args) -> { HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); - return block -> { + return (block, offline) -> { Season season = plugin.getIntegrationManager().getSeason(block.getLocation().getBukkitWorld()); if (season == null) { return false; @@ -625,52 +628,73 @@ private void registerSeasonCondition() { private void registerLightCondition() { registerCondition("skylight_more_than", (args) -> { int value = (int) args; - return block -> { + return (block, offline) -> { int light = block.getLocation().getBukkitLocation().getBlock().getLightFromSky(); return value > light; }; }); registerCondition("skylight_less_than", (args) -> { int value = (int) args; - return block -> { + return (block, offline) -> { int light = block.getLocation().getBukkitLocation().getBlock().getLightFromSky(); return value < light; }; }); registerCondition("light_more_than", (args) -> { int value = (int) args; - return block -> { + return (block, offline) -> { int light = block.getLocation().getBukkitLocation().getBlock().getLightLevel(); return value > light; }; }); registerCondition("light_less_than", (args) -> { int value = (int) args; - return block -> { + return (block, offline) -> { int light = block.getLocation().getBukkitLocation().getBlock().getLightLevel(); return value < light; }; }); } + private void registerPointCondition() { + registerCondition("point_more_than", (args) -> { + int value = (int) args; + return (block, offline) -> { + if (block instanceof MemoryCrop crop) { + return crop.getPoint() > value; + } + return false; + }; + }); + registerCondition("point_less_than", (args) -> { + int value = (int) args; + return (block, offline) -> { + if (block instanceof MemoryCrop crop) { + return crop.getPoint() < value; + } + return false; + }; + }); + } + private void registerWaterCondition() { registerCondition("water_more_than", (args) -> { int value = (int) args; - return block -> { + return (block, offline) -> { Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); return worldPot.filter(pot -> pot.getWater() > value).isPresent(); }; }); registerCondition("water_less_than", (args) -> { int value = (int) args; - return block -> { + return (block, offline) -> { Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); return worldPot.filter(pot -> pot.getWater() < value).isPresent(); }; }); registerCondition("moisture_more_than", (args) -> { int value = (int) args; - return block -> { + return (block, offline) -> { Block underBlock = block.getLocation().copy().add(0,-1,0).getBukkitLocation().getBlock(); if (underBlock.getBlockData() instanceof Farmland farmland) { return farmland.getMoisture() > value; @@ -680,7 +704,7 @@ private void registerWaterCondition() { }); registerCondition("moisture_less_than", (args) -> { int value = (int) args; - return block -> { + return (block, offline) -> { Block underBlock = block.getLocation().copy().add(0,-1,0).getBukkitLocation().getBlock(); if (underBlock.getBlockData() instanceof Farmland farmland) { return farmland.getMoisture() < value; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java index 77de90552..f0460629d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java @@ -25,7 +25,7 @@ public class EmptyCondition implements Condition { public static EmptyCondition instance = new EmptyCondition(); @Override - public boolean isConditionMet(CustomCropsBlock block) { + public boolean isConditionMet(CustomCropsBlock block, boolean offline) { return true; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java index 1f6b77215..fe9188214 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java @@ -20,9 +20,9 @@ import net.momirealms.customcrops.api.manager.VersionManager; import net.momirealms.customcrops.api.mechanic.misc.CRotation; import net.momirealms.customcrops.api.util.LocationUtils; -import net.momirealms.customcrops.utils.ConfigUtils; -import net.momirealms.customcrops.utils.DisplayEntityUtils; -import net.momirealms.customcrops.utils.RotationUtils; +import net.momirealms.customcrops.util.ConfigUtils; +import net.momirealms.customcrops.util.DisplayEntityUtils; +import net.momirealms.customcrops.util.RotationUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 97e00859d..1a1428b35 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -57,10 +57,10 @@ import net.momirealms.customcrops.mechanic.item.impl.WateringCanConfig; import net.momirealms.customcrops.mechanic.item.impl.fertilizer.*; import net.momirealms.customcrops.mechanic.world.block.*; -import net.momirealms.customcrops.utils.ConfigUtils; -import net.momirealms.customcrops.utils.EventUtils; -import net.momirealms.customcrops.utils.ItemUtils; -import net.momirealms.customcrops.utils.RotationUtils; +import net.momirealms.customcrops.util.ConfigUtils; +import net.momirealms.customcrops.util.EventUtils; +import net.momirealms.customcrops.util.ItemUtils; +import net.momirealms.customcrops.util.RotationUtils; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -213,7 +213,7 @@ private void resetItemDetectionOrder() { @Override public String getItemID(ItemStack itemStack) { - if (itemStack == null) + if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) return "AIR"; String id; id = customProvider.getItemID(itemStack); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java index 198aa4100..6a27fef06 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java @@ -27,7 +27,7 @@ import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.utils.EventUtils; +import net.momirealms.customcrops.util.EventUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java index 7604f3d79..62e093949 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java @@ -20,7 +20,6 @@ import dev.lone.itemsadder.api.CustomBlock; import dev.lone.itemsadder.api.CustomFurniture; import dev.lone.itemsadder.api.CustomStack; -import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.mechanic.item.CustomProvider; import org.bukkit.Location; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java index 754cd9b3c..34f2327c5 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java @@ -41,9 +41,7 @@ public boolean removeBlock(Location location) { if (block.getType() == Material.AIR) { return false; } - if (!OraxenBlocks.remove(location, null, false)) { - block.setType(Material.AIR); - } + block.setType(Material.AIR); return true; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java index d2d3abe21..ecda9369c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java @@ -26,7 +26,7 @@ import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; import net.momirealms.customcrops.api.mechanic.requirement.Requirement; import net.momirealms.customcrops.mechanic.item.AbstractEventItem; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.util.ConfigUtils; import java.util.HashMap; import java.util.HashSet; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java index ba2333cf6..72b1b1955 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java @@ -22,7 +22,7 @@ import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.scheduler.CancellableTask; import net.momirealms.customcrops.manager.PacketManager; -import net.momirealms.customcrops.utils.FakeEntityUtils; +import net.momirealms.customcrops.util.FakeEntityUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.EntityType; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java index 871e13eed..fb74bbd9f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java @@ -21,7 +21,7 @@ import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.manager.PacketManager; -import net.momirealms.customcrops.utils.FakeEntityUtils; +import net.momirealms.customcrops.util.FakeEntityUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.EntityType; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/migrator/Migration.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/migrator/Migration.java index cefe5813e..dac5ecb2e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/migrator/Migration.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/migrator/Migration.java @@ -11,7 +11,7 @@ import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.mechanic.world.CWorld; import net.momirealms.customcrops.mechanic.world.adaptor.BukkitWorldAdaptor; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; @@ -47,7 +47,7 @@ private static void doV343Migration() { if (CustomCropsPlugin.get().getWorldManager().getWorldAdaptor() instanceof BukkitWorldAdaptor adaptor) { for (World world : Bukkit.getWorlds()) { CWorld temp = new CWorld(CustomCropsPlugin.getInstance().getWorldManager(), world); - temp.setWorldSetting(WorldSetting.of(false,300,true, 1,true,2,true,2,false,false,false,28,-1,-1,-1, 0)); + temp.setWorldSetting(WorldSetting.of(false,300,true, 1,true,2,true,2,false,1200, false,false,28,-1,-1,-1, 0)); adaptor.convertWorldFromV342toV343(temp, world); } } @@ -102,7 +102,7 @@ private static void doV33Migration(YamlConfiguration config) { if (CustomCropsPlugin.get().getWorldManager().getWorldAdaptor() instanceof BukkitWorldAdaptor adaptor) { for (World world : Bukkit.getWorlds()) { CWorld temp = new CWorld(CustomCropsPlugin.getInstance().getWorldManager(), world); - temp.setWorldSetting(WorldSetting.of(false,300,true, 1,true,2,true,2,false,false,false,28,-1,-1,-1, 0)); + temp.setWorldSetting(WorldSetting.of(false,300,true, 1,true,2,true,2,false, 1200, false,false,28,-1,-1,-1, 0)); adaptor.convertWorldFromV33toV34(temp, world); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java index ec28e0e91..9125b512e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java @@ -18,7 +18,7 @@ package net.momirealms.customcrops.mechanic.misc.value; import net.momirealms.customcrops.api.mechanic.misc.Value; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.entity.Player; import java.util.HashMap; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java index 2f87b14ff..73c967f1f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java @@ -37,8 +37,8 @@ import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.compatibility.VaultHook; import net.momirealms.customcrops.compatibility.papi.ParseUtils; -import net.momirealms.customcrops.utils.ClassUtils; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.util.ClassUtils; +import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java index 2555f1380..1bac59ba0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -88,7 +88,26 @@ public void updateLastLoadedTime() { @Override public void notifyOfflineUpdates() { - this.lastLoadedTime = System.currentTimeMillis(); + long current = System.currentTimeMillis(); + int offlineTimeInSeconds = (int) (this.lastLoadedTime - current) / 1000; + offlineTimeInSeconds = Math.min(offlineTimeInSeconds, cWorld.getWorldSetting().getMaxOfflineTime()); + this.lastLoadedTime = current; + var setting = cWorld.getWorldSetting(); + int minTickUnit = setting.getMinTickUnit(); + + for (int i = 0; i < offlineTimeInSeconds; i++) { + this.loadedSeconds++; + if (this.loadedSeconds >= minTickUnit) { + this.loadedSeconds = 0; + this.tickedBlocks.clear(); + this.queue.clear(); + if (setting.isScheduledTick()) { + this.arrangeTasks(minTickUnit); + } + } + scheduledTick(setting, true); + randomTick(setting, true); + } } public void setWorld(CWorld cWorld) { @@ -121,10 +140,15 @@ public void secondTimer() { this.tickedBlocks.clear(); this.queue.clear(); if (setting.isScheduledTick()) { - this.arrangeTasks(setting.getMinTickUnit()); + this.arrangeTasks(interval); } } + scheduledTick(setting, false); + randomTick(setting, false); + } + + private void scheduledTick(WorldSetting setting, boolean offline) { // scheduled tick while (!queue.isEmpty() && queue.peek().getTime() <= loadedSeconds) { TickTask task = queue.poll(); @@ -138,24 +162,26 @@ public void secondTimer() { case SCARECROW, GREENHOUSE -> {} case POT -> { if (!setting.randomTickPot()) { - block.tick(setting.getTickPotInterval()); + block.tick(setting.getTickPotInterval(), offline); } } case CROP -> { if (!setting.randomTickCrop()) { - block.tick(setting.getTickCropInterval()); + block.tick(setting.getTickCropInterval(), offline); } } case SPRINKLER -> { if (!setting.randomTickSprinkler()) { - block.tick(setting.getTickSprinklerInterval()); + block.tick(setting.getTickSprinklerInterval(), offline); } } } } } } + } + private void randomTick(WorldSetting setting, boolean offline) { // random tick ThreadLocalRandom random = ThreadLocalRandom.current(); int randomTicks = setting.getRandomTickSpeed(); @@ -171,18 +197,18 @@ public void secondTimer() { switch (block.getType()) { case CROP -> { if (setting.randomTickCrop()) { - block.tick(setting.getTickCropInterval()); + block.tick(setting.getTickCropInterval(), offline); } } case SPRINKLER -> { if (setting.randomTickSprinkler()) { - block.tick(setting.getTickSprinklerInterval()); + block.tick(setting.getTickSprinklerInterval(), offline); } } case POT -> { ((WorldPot) block).tickWater(this); if (setting.randomTickPot()) { - block.tick(setting.getTickPotInterval()); + block.tick(setting.getTickPotInterval(), offline); } } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index 052024d6d..721724cf5 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -36,7 +36,7 @@ import net.momirealms.customcrops.api.scheduler.CancellableTask; import net.momirealms.customcrops.api.scheduler.Scheduler; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.utils.EventUtils; +import net.momirealms.customcrops.util.EventUtils; import org.bukkit.Bukkit; import org.bukkit.World; import org.jetbrains.annotations.Nullable; @@ -122,13 +122,19 @@ private void timer() { this.updateSeasonAndDate(); } if (setting.isSchedulerEnabled()) { + Scheduler scheduler = CustomCropsPlugin.get().getScheduler(); if (VersionManager.folia()) { - Scheduler scheduler = CustomCropsPlugin.get().getScheduler(); for (CChunk chunk : loadedChunks.values()) { + if (unloadIfNotLoaded(chunk.getChunkPos())) { + continue; + } scheduler.runTaskSync(chunk::secondTimer, getWorld(), chunk.getChunkPos().x(), chunk.getChunkPos().z()); } } else { for (CChunk chunk : loadedChunks.values()) { + if (unloadIfNotLoaded(chunk.getChunkPos())) { + continue; + } chunk.secondTimer(); } } @@ -302,49 +308,49 @@ public Season getSeason() { @Override public Optional getSprinklerAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + CChunk chunk = loadedChunks.get(location.getChunkPos()); if (chunk == null) return Optional.empty(); return chunk.getSprinklerAt(location); } @Override public Optional getPotAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + CChunk chunk = loadedChunks.get(location.getChunkPos()); if (chunk == null) return Optional.empty(); return chunk.getPotAt(location); } @Override public Optional getCropAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + CChunk chunk = loadedChunks.get(location.getChunkPos()); if (chunk == null) return Optional.empty(); return chunk.getCropAt(location); } @Override public Optional getGlassAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + CChunk chunk = loadedChunks.get(location.getChunkPos()); if (chunk == null) return Optional.empty(); return chunk.getGlassAt(location); } @Override public Optional getScarecrowAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + CChunk chunk = loadedChunks.get(location.getChunkPos()); if (chunk == null) return Optional.empty(); return chunk.getScarecrowAt(location); } @Override public Optional getBlockAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + CChunk chunk = loadedChunks.get(location.getChunkPos()); if (chunk == null) return Optional.empty(); return chunk.getBlockAt(location); } @Override public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addWaterToSprinkler(sprinkler, location, amount); } else { @@ -354,7 +360,7 @@ public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, in @Override public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addFertilizerToPot(pot, fertilizer, location); } else { @@ -364,7 +370,7 @@ public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation lo @Override public void addWaterToPot(Pot pot, SimpleLocation location, int amount) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addWaterToPot(pot, location, amount); } else { @@ -374,7 +380,7 @@ public void addWaterToPot(Pot pot, SimpleLocation location, int amount) { @Override public void addPotAt(WorldPot pot, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addPotAt(pot, location); } else { @@ -384,7 +390,7 @@ public void addPotAt(WorldPot pot, SimpleLocation location) { @Override public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addSprinklerAt(sprinkler, location); } else { @@ -394,7 +400,7 @@ public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) { @Override public void addCropAt(WorldCrop crop, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addCropAt(crop, location); } else { @@ -404,7 +410,7 @@ public void addCropAt(WorldCrop crop, SimpleLocation location) { @Override public void addPointToCrop(Crop crop, SimpleLocation location, int points) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addPointToCrop(crop, location, points); } else { @@ -414,7 +420,7 @@ public void addPointToCrop(Crop crop, SimpleLocation location, int points) { @Override public void addGlassAt(WorldGlass glass, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addGlassAt(glass, location); } else { @@ -424,7 +430,7 @@ public void addGlassAt(WorldGlass glass, SimpleLocation location) { @Override public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addScarecrowAt(scarecrow, location); } else { @@ -434,7 +440,7 @@ public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { @Override public void removeSprinklerAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().removeSprinklerAt(location); } else { @@ -444,7 +450,7 @@ public void removeSprinklerAt(SimpleLocation location) { @Override public void removePotAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().removePotAt(location); } else { @@ -454,7 +460,7 @@ public void removePotAt(SimpleLocation location) { @Override public void removeCropAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().removeCropAt(location); } else { @@ -464,7 +470,7 @@ public void removeCropAt(SimpleLocation location) { @Override public void removeGlassAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().removeGlassAt(location); } else { @@ -474,7 +480,7 @@ public void removeGlassAt(SimpleLocation location) { @Override public void removeScarecrowAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().removeScarecrowAt(location); } else { @@ -484,7 +490,7 @@ public void removeScarecrowAt(SimpleLocation location) { @Override public CustomCropsBlock removeAnythingAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { return chunk.get().removeBlockAt(location); } else { @@ -514,7 +520,7 @@ private CustomCropsChunk createOrGetChunk(ChunkPos chunkPos) { @Override public boolean isPotReachLimit(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isEmpty()) { LogUtils.warn("Invalid operation: Querying pot amount from a not generated chunk"); return true; @@ -525,7 +531,7 @@ public boolean isPotReachLimit(SimpleLocation location) { @Override public boolean isCropReachLimit(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isEmpty()) { LogUtils.warn("Invalid operation: Querying crop amount from a not generated chunk"); return true; @@ -536,7 +542,7 @@ public boolean isCropReachLimit(SimpleLocation location) { @Override public boolean isSprinklerReachLimit(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkCoordinate()); + Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isEmpty()) { LogUtils.warn("Invalid operation: Querying sprinkler amount from a not generated chunk"); return true; @@ -559,4 +565,12 @@ private boolean isRegionNoLongerLoaded(RegionPos region) { } return true; } + + private boolean unloadIfNotLoaded(ChunkPos pos) { + if (!world.get().isChunkLoaded(pos.x(), pos.z())) { + unloadChunk(pos); + return true; + } + return false; + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index 24e215be6..a9800bf97 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -29,7 +29,7 @@ import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.mechanic.world.adaptor.BukkitWorldAdaptor; import net.momirealms.customcrops.mechanic.world.adaptor.SlimeWorldAdaptor; -import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; @@ -490,6 +490,7 @@ public void handleChunkLoad(Chunk bukkitChunk) { } CustomCropsChunk chunk = optionalChunk.get(); + // load the entities if not loaded bukkitChunk.getEntities(); chunk.notifyOfflineUpdates(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java index 13094a4c8..7a3dec945 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java @@ -101,13 +101,13 @@ public int hashCode() { } @Override - public void tick(int interval) { + public void tick(int interval, boolean offline) { if (canTick(interval)) { - tick(); + tick(offline); } } - private void tick() { + private void tick(boolean offline) { Crop crop = getConfig(); if (crop == null) { LogUtils.warn("Found a crop without config at " + getLocation() + ". Try removing the data."); @@ -124,14 +124,14 @@ private void tick() { // check death conditions for (DeathConditions deathConditions : crop.getDeathConditions()) { for (Condition condition : deathConditions.getConditions()) { - if (condition.isConditionMet(this)) { + if (condition.isConditionMet(this, offline)) { CustomCropsPlugin.get().getScheduler().runTaskSyncLater(() -> { CustomCropsPlugin.get().getWorldManager().removeCropAt(location); CustomCropsPlugin.get().getItemManager().removeAnythingAt(bukkitLocation); if (deathConditions.getDeathItem() != null) { CustomCropsPlugin.get().getItemManager().placeItem(bukkitLocation, deathConditions.getItemCarrier(), deathConditions.getDeathItem()); } - }, bukkitLocation, deathConditions.getDeathDelay()); + }, bukkitLocation, offline ? 0 : deathConditions.getDeathDelay()); return; } } @@ -144,7 +144,7 @@ private void tick() { // check grow conditions for (Condition condition : crop.getGrowConditions().getConditions()) { - if (!condition.isConditionMet(this)) { + if (!condition.isConditionMet(this, offline)) { return; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java index b00ffb4e7..0d21d70f0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java @@ -54,7 +54,7 @@ public int hashCode() { } @Override - public void tick(int interval) { + public void tick(int interval, boolean offline) { } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java index 49fefedf9..9f61ea691 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java @@ -189,12 +189,13 @@ public int hashCode() { } @Override - public void tick(int interval) { + public void tick(int interval, boolean offline) { if (canTick(interval)) { tick(); } } + // if the tick is triggered by offline growth private void tick() { Pot pot = getConfig(); if (pot == null) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java index 0c78c170a..001192255 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java @@ -54,7 +54,7 @@ public int hashCode() { } @Override - public void tick(int interval) { + public void tick(int interval, boolean offline) { } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java index 8cb187e9f..ba9cac0bf 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java @@ -26,6 +26,7 @@ import net.momirealms.customcrops.api.mechanic.item.Pot; import net.momirealms.customcrops.api.mechanic.item.Sprinkler; import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.mechanic.world.ChunkPos; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; @@ -34,10 +35,10 @@ import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.inventory.ItemStack; -import java.util.Objects; -import java.util.Optional; +import java.util.*; public class MemorySprinkler extends AbstractCustomCropsBlock implements WorldSprinkler { @@ -97,13 +98,14 @@ public int hashCode() { } @Override - public void tick(int interval) { + public void tick(int interval, boolean offline) { if (canTick(interval)) { - tick(); + tick(offline); } } - private void tick() { + // if the tick is triggered by offline growth + private void tick(boolean offline) { Sprinkler sprinkler = getConfig(); if (sprinkler == null) { LogUtils.warn("Found a sprinkler without config at " + getLocation() + ". Try removing the data."); @@ -127,7 +129,9 @@ private void tick() { Location bukkitLocation = location.getBukkitLocation(); if (bukkitLocation == null) return; CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { - sprinkler.trigger(ActionTrigger.WORK, new State(null, new ItemStack(Material.AIR), bukkitLocation)); + State state = new State(null, new ItemStack(Material.AIR), bukkitLocation); + if (offline) state.setArg("{offline}", "true"); + sprinkler.trigger(ActionTrigger.WORK, state); if (updateState && sprinkler.get3DItemWithWater() != null) { CustomCropsPlugin.get().getItemManager().removeAnythingAt(bukkitLocation); CustomCropsPlugin.get().getItemManager().placeItem(bukkitLocation, sprinkler.getItemCarrier(), sprinkler.get3DItemID()); @@ -135,11 +139,26 @@ private void tick() { }, bukkitLocation); int range = sprinkler.getRange(); - CustomCropsWorld world = CustomCropsPlugin.get().getWorldManager().getCustomCropsWorld(location.getWorldName()).get(); + HashMap> map = new HashMap<>(); for (int i = -range; i <= range; i++) { for (int j = -range; j <= range; j++) { for (int k : new int[]{-1,0}) { SimpleLocation potLocation = location.copy().add(i,k,j); + var cPos = potLocation.getChunkPos(); + var list = map.computeIfAbsent(cPos, key -> new ArrayList<>()); + list.add(potLocation); + } + } + } + + CustomCropsWorld world = CustomCropsPlugin.get().getWorldManager().getCustomCropsWorld(location.getWorldName()).get(); + World bkWorld = world.getWorld(); + for (Map.Entry> entry : map.entrySet()) { + var chunkPos = entry.getKey(); + CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { + // load the chunk firstly to load CustomCrops data + bkWorld.getChunkAt(chunkPos.x(), chunkPos.z()); + for (SimpleLocation potLocation : entry.getValue()) { Optional pot = world.getPotAt(potLocation); if (pot.isPresent()) { WorldPot worldPot = pot.get(); @@ -152,13 +171,13 @@ private void tick() { } worldPot.setWater(current + sprinkler.getWater()); if (current == 0) { - CustomCropsPlugin.get().getScheduler().runTaskSync(() -> CustomCropsPlugin.get().getItemManager().updatePotState(potLocation.getBukkitLocation(), potConfig, true, worldPot.getFertilizer()), potLocation.getBukkitLocation()); + CustomCropsPlugin.get().getItemManager().updatePotState(potLocation.getBukkitLocation(), potConfig, true, worldPot.getFertilizer()); } } } } } - } + }, bkWorld, chunkPos.x(), chunkPos.z()); } } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/ClassUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ClassUtils.java similarity index 98% rename from plugin/src/main/java/net/momirealms/customcrops/utils/ClassUtils.java rename to plugin/src/main/java/net/momirealms/customcrops/util/ClassUtils.java index 6cd18acad..b558e6b44 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/utils/ClassUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/ClassUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.utils; +package net.momirealms.customcrops.util; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java similarity index 98% rename from plugin/src/main/java/net/momirealms/customcrops/utils/ConfigUtils.java rename to plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java index ba5af1e2b..7ae82127c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/utils/ConfigUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.utils; +package net.momirealms.customcrops.util; import com.google.common.base.Preconditions; import net.momirealms.customcrops.api.CustomCropsPlugin; @@ -70,7 +70,8 @@ public static WorldSetting getWorldSettingFromSection(ConfigurationSection secti section.getInt("pot.tick-interval", 2), getRandomTickModeByString(section.getString("sprinkler.mode")), section.getInt("sprinkler.tick-interval", 2), - section.getBoolean("offline-grow", false), + section.getBoolean("offline-growth.enable", false), + section.getInt("offline-growth.max-offline-seconds", 1200), section.getBoolean("season.enable", false), section.getBoolean("season.auto-alternation", false), section.getInt("season.duration", 28), diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/DisplayEntityUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java similarity index 90% rename from plugin/src/main/java/net/momirealms/customcrops/utils/DisplayEntityUtils.java rename to plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java index e23914eb9..d1f0825e5 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/utils/DisplayEntityUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java @@ -1,4 +1,4 @@ -package net.momirealms.customcrops.utils; +package net.momirealms.customcrops.util; import net.momirealms.customcrops.api.mechanic.misc.CRotation; import org.bukkit.entity.Entity; diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/EventUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/EventUtils.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/utils/EventUtils.java rename to plugin/src/main/java/net/momirealms/customcrops/util/EventUtils.java index 02e02da35..2f503b95e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/utils/EventUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/EventUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.utils; +package net.momirealms.customcrops.util; import org.bukkit.Bukkit; import org.bukkit.event.Cancellable; diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/FakeEntityUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java similarity index 99% rename from plugin/src/main/java/net/momirealms/customcrops/utils/FakeEntityUtils.java rename to plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java index 52f97dce8..030c2a99a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/utils/FakeEntityUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.utils; +package net.momirealms.customcrops.util; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.events.PacketContainer; diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/ItemUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java similarity index 99% rename from plugin/src/main/java/net/momirealms/customcrops/utils/ItemUtils.java rename to plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java index da89378bc..175872ccb 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/utils/ItemUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.utils; +package net.momirealms.customcrops.util; import de.tr7zw.changeme.nbtapi.NBTItem; import org.bukkit.Bukkit; diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/RotationUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java similarity index 98% rename from plugin/src/main/java/net/momirealms/customcrops/utils/RotationUtils.java rename to plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java index fd7c07d70..abf1eb546 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/utils/RotationUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.utils; +package net.momirealms.customcrops.util; import net.momirealms.customcrops.api.mechanic.misc.CRotation; import org.bukkit.Rotation; diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index fc0669e73..f94b33d56 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -46,6 +46,16 @@ worlds: # For crops, under the same conditions, the growth rate of crops is basically the same # For sprinklers and pots, they would work periodically. min-tick-unit: 300 + # Offline growth settings + # This option allows crops to grow even if the world is unloaded or the server is closed + # This may lead to some issues caused by timeliness conditions for instance seasons + offline-growth: + enable: false + # Maximum offline time recorded in seconds + # Please do not set this option to a value that is too large, + # as it may cause chunks that have been unloaded for a long time + # taking long to load + max-offline-seconds: 1200 # Settings for crops crop: # [RANDOM_TICK] From 8d8e311bfdfd88a40ddc0b74e600a9d795b376e9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Wed, 3 Apr 2024 19:08:33 +0800 Subject: [PATCH 006/329] changed config keys --- .../api/mechanic/world/level/WorldSetting.java | 10 +++++----- .../momirealms/customcrops/mechanic/world/CChunk.java | 1 - .../customcrops/mechanic/world/WorldManagerImpl.java | 2 +- .../net/momirealms/customcrops/util/ConfigUtils.java | 4 ++-- plugin/src/main/resources/config.yml | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java index da4b26809..e9a9b0df5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java @@ -24,7 +24,7 @@ public class WorldSetting implements Cloneable { private final int tickCropInterval; private final int tickPotInterval; private final int tickSprinklerInterval; - private final boolean offlineGrow; + private final boolean offlineTick; private final boolean enableSeason; private final boolean autoSeasonChange; private final int seasonDuration; @@ -47,7 +47,7 @@ private WorldSetting( int tickPotInterval, boolean tickSprinklerRandomly, int tickSprinklerInterval, - boolean offlineGrow, + boolean offlineTick, int maxOfflineTime, boolean enableSeason, boolean autoSeasonChange, @@ -62,7 +62,7 @@ private WorldSetting( this.tickCropInterval = tickCropInterval; this.tickPotInterval = tickPotInterval; this.tickSprinklerInterval = tickSprinklerInterval; - this.offlineGrow = offlineGrow; + this.offlineTick = offlineTick; this.maxOfflineTime = maxOfflineTime; this.enableSeason = enableSeason; this.autoSeasonChange = autoSeasonChange; @@ -137,8 +137,8 @@ public int getTickSprinklerInterval() { return tickSprinklerInterval; } - public boolean isOfflineGrow() { - return offlineGrow; + public boolean isOfflineTick() { + return offlineTick; } public boolean isEnableSeason() { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java index 1bac59ba0..60743b01a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -94,7 +94,6 @@ public void notifyOfflineUpdates() { this.lastLoadedTime = current; var setting = cWorld.getWorldSetting(); int minTickUnit = setting.getMinTickUnit(); - for (int i = 0; i < offlineTimeInSeconds; i++) { this.loadedSeconds++; if (this.loadedSeconds >= minTickUnit) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index a9800bf97..e8d9ea268 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -481,7 +481,7 @@ public void handleChunkLoad(Chunk bukkitChunk) { this.worldAdaptor.loadChunkData(customCropsWorld, chunkPos); // offline grow part - if (!customCropsWorld.getWorldSetting().isOfflineGrow()) return; + if (!customCropsWorld.getWorldSetting().isOfflineTick()) return; // If chunk data not exists, return Optional optionalChunk = customCropsWorld.getLoadedChunkAt(chunkPos); diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java index 7ae82127c..dc08207df 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java @@ -70,8 +70,8 @@ public static WorldSetting getWorldSettingFromSection(ConfigurationSection secti section.getInt("pot.tick-interval", 2), getRandomTickModeByString(section.getString("sprinkler.mode")), section.getInt("sprinkler.tick-interval", 2), - section.getBoolean("offline-growth.enable", false), - section.getInt("offline-growth.max-offline-seconds", 1200), + section.getBoolean("offline-tick.enable", false), + section.getInt("offline-tick.max-offline-seconds", 1200), section.getBoolean("season.enable", false), section.getBoolean("season.auto-alternation", false), section.getInt("season.duration", 28), diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index f94b33d56..182af7da5 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -46,10 +46,10 @@ worlds: # For crops, under the same conditions, the growth rate of crops is basically the same # For sprinklers and pots, they would work periodically. min-tick-unit: 300 - # Offline growth settings + # Offline tick settings # This option allows crops to grow even if the world is unloaded or the server is closed # This may lead to some issues caused by timeliness conditions for instance seasons - offline-growth: + offline-tick: enable: false # Maximum offline time recorded in seconds # Please do not set this option to a value that is too large, From 6303649782b6775b9ea5f69278b4e6b8ed4f1feb Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Wed, 3 Apr 2024 20:52:00 +0800 Subject: [PATCH 007/329] fix some chunk bugs --- .../world/level/CustomCropsWorld.java | 2 + .../customcrops/mechanic/world/CChunk.java | 3 +- .../customcrops/mechanic/world/CWorld.java | 43 +++++++++++-------- .../scheduler/FoliaSchedulerImpl.java | 6 +-- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java index c70e4e66f..ab70de72f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java @@ -56,6 +56,8 @@ public interface CustomCropsWorld { boolean isChunkLoaded(ChunkPos chunkPos); + Optional getOrCreateLoadedChunkAt(ChunkPos chunkPos); + Optional getLoadedChunkAt(ChunkPos chunkPos); Optional getLoadedRegionAt(RegionPos regionPos); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java index 60743b01a..293341125 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -89,7 +89,8 @@ public void updateLastLoadedTime() { @Override public void notifyOfflineUpdates() { long current = System.currentTimeMillis(); - int offlineTimeInSeconds = (int) (this.lastLoadedTime - current) / 1000; + int offlineTimeInSeconds = (int) (current - this.lastLoadedTime) / 1000; + CustomCropsPlugin.get().debug("offlineSeconds: " + offlineTimeInSeconds + "s. " + chunkPos.toString()); offlineTimeInSeconds = Math.min(offlineTimeInSeconds, cWorld.getWorldSetting().getMaxOfflineTime()); this.lastLoadedTime = current; var setting = cWorld.getWorldSetting(); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index 721724cf5..da960b90a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -233,10 +233,15 @@ public boolean isRegionLoaded(RegionPos regionPos) { } @Override - public Optional getLoadedChunkAt(ChunkPos chunkPos) { + public Optional getOrCreateLoadedChunkAt(ChunkPos chunkPos) { return Optional.ofNullable(createOrGetChunk(chunkPos)); } + @Override + public Optional getLoadedChunkAt(ChunkPos chunkPos) { + return Optional.ofNullable(loadedChunks.get(chunkPos)); + } + @Override public Optional getLoadedRegionAt(RegionPos regionPos) { return Optional.ofNullable(loadedRegions.get(regionPos)); @@ -350,7 +355,7 @@ public Optional getBlockAt(SimpleLocation location) { @Override public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addWaterToSprinkler(sprinkler, location, amount); } else { @@ -360,7 +365,7 @@ public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, in @Override public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addFertilizerToPot(pot, fertilizer, location); } else { @@ -370,7 +375,7 @@ public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation lo @Override public void addWaterToPot(Pot pot, SimpleLocation location, int amount) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addWaterToPot(pot, location, amount); } else { @@ -380,7 +385,7 @@ public void addWaterToPot(Pot pot, SimpleLocation location, int amount) { @Override public void addPotAt(WorldPot pot, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addPotAt(pot, location); } else { @@ -390,7 +395,7 @@ public void addPotAt(WorldPot pot, SimpleLocation location) { @Override public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addSprinklerAt(sprinkler, location); } else { @@ -400,7 +405,7 @@ public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) { @Override public void addCropAt(WorldCrop crop, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addCropAt(crop, location); } else { @@ -410,7 +415,7 @@ public void addCropAt(WorldCrop crop, SimpleLocation location) { @Override public void addPointToCrop(Crop crop, SimpleLocation location, int points) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addPointToCrop(crop, location, points); } else { @@ -420,7 +425,7 @@ public void addPointToCrop(Crop crop, SimpleLocation location, int points) { @Override public void addGlassAt(WorldGlass glass, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addGlassAt(glass, location); } else { @@ -430,7 +435,7 @@ public void addGlassAt(WorldGlass glass, SimpleLocation location) { @Override public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); + Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { chunk.get().addScarecrowAt(scarecrow, location); } else { @@ -444,7 +449,7 @@ public void removeSprinklerAt(SimpleLocation location) { if (chunk.isPresent()) { chunk.get().removeSprinklerAt(location); } else { - LogUtils.warn("Invalid operation: Removing sprinkler from a not generated chunk"); + LogUtils.warn("Invalid operation: Removing sprinkler from an unloaded chunk"); } } @@ -454,7 +459,7 @@ public void removePotAt(SimpleLocation location) { if (chunk.isPresent()) { chunk.get().removePotAt(location); } else { - LogUtils.warn("Invalid operation: Removing pot from a not generated chunk"); + LogUtils.warn("Invalid operation: Removing pot from an unloaded chunk"); } } @@ -464,7 +469,7 @@ public void removeCropAt(SimpleLocation location) { if (chunk.isPresent()) { chunk.get().removeCropAt(location); } else { - LogUtils.warn("Invalid operation: Removing crop from a not generated chunk"); + LogUtils.warn("Invalid operation: Removing crop from an unloaded chunk"); } } @@ -474,7 +479,7 @@ public void removeGlassAt(SimpleLocation location) { if (chunk.isPresent()) { chunk.get().removeGlassAt(location); } else { - LogUtils.warn("Invalid operation: Removing glass from a not generated chunk"); + LogUtils.warn("Invalid operation: Removing glass from an unloaded chunk"); } } @@ -484,7 +489,7 @@ public void removeScarecrowAt(SimpleLocation location) { if (chunk.isPresent()) { chunk.get().removeScarecrowAt(location); } else { - LogUtils.warn("Invalid operation: Removing scarecrow from a not generated chunk"); + LogUtils.warn("Invalid operation: Removing scarecrow from an unloaded chunk"); } } @@ -494,7 +499,7 @@ public CustomCropsBlock removeAnythingAt(SimpleLocation location) { if (chunk.isPresent()) { return chunk.get().removeBlockAt(location); } else { - LogUtils.warn("Invalid operation: Removing anything from a not generated chunk"); + LogUtils.warn("Invalid operation: Removing anything from an unloaded chunk"); return null; } } @@ -522,7 +527,7 @@ private CustomCropsChunk createOrGetChunk(ChunkPos chunkPos) { public boolean isPotReachLimit(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isEmpty()) { - LogUtils.warn("Invalid operation: Querying pot amount from a not generated chunk"); + LogUtils.warn("Invalid operation: Querying pot amount from an unloaded chunk"); return true; } if (setting.getPotPerChunk() < 0) return false; @@ -533,7 +538,7 @@ public boolean isPotReachLimit(SimpleLocation location) { public boolean isCropReachLimit(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isEmpty()) { - LogUtils.warn("Invalid operation: Querying crop amount from a not generated chunk"); + LogUtils.warn("Invalid operation: Querying crop amount from an unloaded chunk"); return true; } if (setting.getCropPerChunk() < 0) return false; @@ -544,7 +549,7 @@ public boolean isCropReachLimit(SimpleLocation location) { public boolean isSprinklerReachLimit(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isEmpty()) { - LogUtils.warn("Invalid operation: Querying sprinkler amount from a not generated chunk"); + LogUtils.warn("Invalid operation: Querying sprinkler amount from an unloaded chunk"); return true; } if (setting.getSprinklerPerChunk() < 0) return false; diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java index 9b6ce82ff..61c787e56 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java @@ -57,10 +57,8 @@ public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, lo @Override public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { if (delay == 0) { - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().run(plugin, (scheduledTask -> runnable.run()))); - } - return new FoliaCancellableTask(Bukkit.getRegionScheduler().run(plugin, location, (scheduledTask -> runnable.run()))); + runSyncTask(runnable, location); + return new FoliaCancellableTask(null); } if (location == null) { return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, (scheduledTask -> runnable.run()), delay)); From 6887c1a48dddfb186dd5dabc370ab8c625625ef2 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Thu, 4 Apr 2024 22:41:15 +0800 Subject: [PATCH 008/329] Fix some warnings --- build.gradle.kts | 2 +- .../customcrops/mechanic/world/CChunk.java | 2 +- .../customcrops/mechanic/world/CWorld.java | 15 +++------------ .../world/adaptor/BukkitWorldAdaptor.java | 11 ++++------- 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6573a7da7..99ca9d482 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.4" + project.version = "3.4.4.1" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java index 293341125..a5861ea2f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -90,7 +90,7 @@ public void updateLastLoadedTime() { public void notifyOfflineUpdates() { long current = System.currentTimeMillis(); int offlineTimeInSeconds = (int) (current - this.lastLoadedTime) / 1000; - CustomCropsPlugin.get().debug("offlineSeconds: " + offlineTimeInSeconds + "s. " + chunkPos.toString()); + CustomCropsPlugin.get().debug(chunkPos.toString() + " Offline seconds: " + offlineTimeInSeconds + "s."); offlineTimeInSeconds = Math.min(offlineTimeInSeconds, cWorld.getWorldSetting().getMaxOfflineTime()); this.lastLoadedTime = current; var setting = cWorld.getWorldSetting(); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index da960b90a..a43884dab 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -526,10 +526,7 @@ private CustomCropsChunk createOrGetChunk(ChunkPos chunkPos) { @Override public boolean isPotReachLimit(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isEmpty()) { - LogUtils.warn("Invalid operation: Querying pot amount from an unloaded chunk"); - return true; - } + if (chunk.isEmpty()) return false; if (setting.getPotPerChunk() < 0) return false; return chunk.get().getPotAmount() >= setting.getPotPerChunk(); } @@ -537,10 +534,7 @@ public boolean isPotReachLimit(SimpleLocation location) { @Override public boolean isCropReachLimit(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isEmpty()) { - LogUtils.warn("Invalid operation: Querying crop amount from an unloaded chunk"); - return true; - } + if (chunk.isEmpty()) return false; if (setting.getCropPerChunk() < 0) return false; return chunk.get().getCropAmount() >= setting.getCropPerChunk(); } @@ -548,10 +542,7 @@ public boolean isCropReachLimit(SimpleLocation location) { @Override public boolean isSprinklerReachLimit(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isEmpty()) { - LogUtils.warn("Invalid operation: Querying sprinkler amount from an unloaded chunk"); - return true; - } + if (chunk.isEmpty()) return false; if (setting.getSprinklerPerChunk() < 0) return false; return chunk.get().getSprinklerAmount() >= setting.getSprinklerPerChunk(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java index ef5569dff..41cb7130b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java @@ -191,7 +191,9 @@ public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) // if region file not exist, create one File data = getRegionDataFilePath(world, regionPos); if (!data.exists()) { - cWorld.loadRegion(new CRegion(cWorld, regionPos)); + var region = new CRegion(cWorld, regionPos); + saveRegion(region); + cWorld.loadRegion(region); return; } @@ -219,7 +221,7 @@ public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) long time2 = System.currentTimeMillis(); CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load region " + regionPos); } - } catch (IOException e) { + } catch (Exception e) { LogUtils.severe("Failed to load CustomCrops region data at " + chunkPos + ". Deleting corrupted region."); e.printStackTrace(); data.delete(); @@ -252,11 +254,6 @@ public void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk) { @Override public void saveRegion(CustomCropsRegion customCropsRegion) { File file = getRegionDataFilePath(customCropsRegion.getCustomCropsWorld().getWorld(), customCropsRegion.getRegionPos()); - if (customCropsRegion.canPrune()) { - file.delete(); - return; - } - long time1 = System.currentTimeMillis(); try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) { bos.write(serialize(customCropsRegion)); From 1e47a499264795f2c68f8c913660dfa2ef6869cf Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Thu, 4 Apr 2024 22:46:14 +0800 Subject: [PATCH 009/329] fix --- .../momirealms/customcrops/manager/AdventureManagerImpl.java | 2 +- .../customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java index 6ba95108c..486153ed9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java @@ -158,7 +158,7 @@ public String legacyToMiniMessage(String legacy) { case 'd' -> stringBuilder.append(""); case 'e' -> stringBuilder.append(""); case 'f' -> stringBuilder.append(""); - case 'r' -> stringBuilder.append(""); + case 'r' -> stringBuilder.append(""); case 'l' -> stringBuilder.append(""); case 'm' -> stringBuilder.append(""); case 'o' -> stringBuilder.append(""); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java index 41cb7130b..ab2957057 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java @@ -192,7 +192,6 @@ public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) File data = getRegionDataFilePath(world, regionPos); if (!data.exists()) { var region = new CRegion(cWorld, regionPos); - saveRegion(region); cWorld.loadRegion(region); return; } From 5873af4558722e3e807db579c084aa3cb50fa502 Mon Sep 17 00:00:00 2001 From: jhqwqmc <113768629+jhqwqmc@users.noreply.github.com> Date: Sun, 7 Apr 2024 21:15:09 +0800 Subject: [PATCH 010/329] small changes Fix weather detection issue --- .../mechanic/requirement/RequirementManagerImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java index 73c967f1f..9ba82c1ca 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java @@ -419,9 +419,8 @@ private void registerWeatherRequirement() { return state -> { String currentWeather; World world = state.getLocation().getWorld(); - if (world.hasStorm()) currentWeather = "rainstorm"; + if (world.isClearWeather()) currentWeather = "clear"; else if (world.isThundering()) currentWeather = "thunder"; - else if (world.isClearWeather()) currentWeather = "clear"; else currentWeather = "rain"; for (String weather : weathers) if (weather.equalsIgnoreCase(currentWeather)) From c7c6004300af69146ef0b69d0c3f095021f13780 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 13 Apr 2024 17:06:21 +0800 Subject: [PATCH 011/329] fix bugs --- build.gradle.kts | 1 + gui-plugin/.gitignore | 42 +++++++++++++++++++ gui-plugin/build.gradle.kts | 13 ++++++ .../customcrops/gui/CustomCropsGUI.java | 21 ++++++++++ .../customcrops/gui/GUIManager.java | 16 +++++++ gui-plugin/src/main/resources/config.yml | 0 gui-plugin/src/main/resources/plugin.yml | 9 ++++ .../requirement/RequirementManagerImpl.java | 26 +++++++++--- settings.gradle | 3 +- 9 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 gui-plugin/.gitignore create mode 100644 gui-plugin/build.gradle.kts create mode 100644 gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java create mode 100644 gui-plugin/src/main/java/net/momirealms/customcrops/gui/GUIManager.java create mode 100644 gui-plugin/src/main/resources/config.yml create mode 100644 gui-plugin/src/main/resources/plugin.yml diff --git a/build.gradle.kts b/build.gradle.kts index 99ca9d482..43c14fda6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,6 +43,7 @@ allprojects { maven("https://repo.infernalsuite.com/repository/maven-releases/") maven("https://repo.rapture.pw/repository/maven-releases/") maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") + maven("https://repo.xenondevs.xyz/releases/") } } diff --git a/gui-plugin/.gitignore b/gui-plugin/.gitignore new file mode 100644 index 000000000..b63da4551 --- /dev/null +++ b/gui-plugin/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/gui-plugin/build.gradle.kts b/gui-plugin/build.gradle.kts new file mode 100644 index 000000000..7c655738c --- /dev/null +++ b/gui-plugin/build.gradle.kts @@ -0,0 +1,13 @@ +dependencies { + compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") + compileOnly(project(":api")) + implementation("xyz.xenondevs.invui:invui:1.27") { + exclude("org.jetbrains") + } +} + +tasks { + shadowJar { + relocate ("xyz.xenondevs", "net.momirealms.customcrops.libraries") + } +} \ No newline at end of file diff --git a/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java b/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java new file mode 100644 index 000000000..158ee0b34 --- /dev/null +++ b/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java @@ -0,0 +1,21 @@ +package net.momirealms.customcrops.gui; + +import org.bukkit.plugin.java.JavaPlugin; + +public class CustomCropsGUI extends JavaPlugin { + + private static CustomCropsGUI instance; + + @Override + public void onEnable() { + instance = this; + } + + @Override + public void onDisable() { + } + + public static CustomCropsGUI getInstance() { + return instance; + } +} diff --git a/gui-plugin/src/main/java/net/momirealms/customcrops/gui/GUIManager.java b/gui-plugin/src/main/java/net/momirealms/customcrops/gui/GUIManager.java new file mode 100644 index 000000000..1ae4af480 --- /dev/null +++ b/gui-plugin/src/main/java/net/momirealms/customcrops/gui/GUIManager.java @@ -0,0 +1,16 @@ +package net.momirealms.customcrops.gui; + +import net.momirealms.customcrops.api.event.PotInteractEvent; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class GUIManager implements Listener { + + @EventHandler (ignoreCancelled = true) + public void onInteractPot(PotInteractEvent event) { + if (event.getItemInHand().getType() != Material.AIR) + return; + + } +} diff --git a/gui-plugin/src/main/resources/config.yml b/gui-plugin/src/main/resources/config.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gui-plugin/src/main/resources/plugin.yml b/gui-plugin/src/main/resources/plugin.yml new file mode 100644 index 000000000..9da86ef9b --- /dev/null +++ b/gui-plugin/src/main/resources/plugin.yml @@ -0,0 +1,9 @@ +name: CustomCrops +version: '${version}' +main: net.momirealms.customcrops.gui.CustomCropsGUI +api-version: 1.17 +load: POSTWORLD +authors: [ XiaoMoMi ] +folia-supported: true +depend: + - CustomCrops \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java index 9ba82c1ca..1e0ee34c7 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java @@ -586,6 +586,22 @@ private void registerRegexRequirement() { } private void registerNumberEqualRequirement() { + registerRequirement("=", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (Double.parseDouble(p1) == Double.parseDouble(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at = requirement."); + return EmptyRequirement.instance; + } + }); registerRequirement("==", (args, actions, advanced) -> { if (args instanceof ConfigurationSection section) { String v1 = section.getString("value1", ""); @@ -598,7 +614,7 @@ private void registerNumberEqualRequirement() { return false; }; } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); + LogUtils.warn("Wrong value format found at == requirement."); return EmptyRequirement.instance; } }); @@ -614,7 +630,7 @@ private void registerNumberEqualRequirement() { return false; }; } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); + LogUtils.warn("Wrong value format found at != requirement."); return EmptyRequirement.instance; } }); @@ -857,7 +873,7 @@ private void registerInListRequirement() { return false; }; } else { - LogUtils.warn("Wrong value format found at in-list requirement."); + LogUtils.warn("Wrong value format found at !in-list requirement."); return EmptyRequirement.instance; } }); @@ -946,7 +962,7 @@ private void registerPluginLevelRequirement() { private void registerPotionEffectRequirement() { registerRequirement("potion-effect", (args, actions, advanced) -> { String potions = (String) args; - String[] split = potions.split("(<=|>=|<|>|==)", 2); + String[] split = potions.split("(<=|>=|<|>|==|=)", 2); PotionEffectType type = PotionEffectType.getByName(split[0]); if (type == null) { LogUtils.warn("Potion effect doesn't exist: " + split[0]); @@ -969,7 +985,7 @@ private void registerPotionEffectRequirement() { case ">" -> { if (level > required) result = true; } - case "==" -> { + case "==", "=" -> { if (level == required) result = true; } case "!=" -> { diff --git a/settings.gradle b/settings.gradle index 3b4dbf8f6..a5b54b7a4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ rootProject.name = 'CustomCrops' include(":plugin") include(":api") -include 'legacy-api' +include(":legacy-api") +include(":gui-plugin") From b2d6fab2590adae02bcfb50d54184edfef313324 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 13 Apr 2024 22:25:46 +0800 Subject: [PATCH 012/329] downgrade AureliumSkills dependency --- .../customcrops/gui/CustomCropsGUI.java | 17 +++++++++++++++++ plugin/build.gradle.kts | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java b/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java index 158ee0b34..b6f808c51 100644 --- a/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java +++ b/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java @@ -1,3 +1,20 @@ +/* + * 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.gui; import org.bukkit.plugin.java.JavaPlugin; diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index d5ff9f571..825bbbaf8 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { compileOnly("com.willfp:libreforge:4.48.1") compileOnly("net.Indyuce:MMOCore-API:1.12-SNAPSHOT") - compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.24") + compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.23") compileOnly("com.github.Zrips:Jobs:4.17.2") compileOnly("dev.aurelium:auraskills-api-bukkit:2.0.0-SNAPSHOT") From 769e1141b209ed627b3c19ebccf2fe04eca9dde3 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sun, 14 Apr 2024 20:11:06 +0800 Subject: [PATCH 013/329] rename language files --- plugin/src/main/resources/config.yml | 3 ++- plugin/src/main/resources/messages/{english.yml => en.yml} | 0 plugin/src/main/resources/messages/{spanish.yml => es.yml} | 0 plugin/src/main/resources/messages/{french.yml => fr.yml} | 0 plugin/src/main/resources/messages/{portuguese.yml => pt.yml} | 0 plugin/src/main/resources/messages/{russian.yml => ru.yml} | 0 plugin/src/main/resources/messages/{chinese.yml => zh_cn.yml} | 0 7 files changed, 2 insertions(+), 1 deletion(-) rename plugin/src/main/resources/messages/{english.yml => en.yml} (100%) rename plugin/src/main/resources/messages/{spanish.yml => es.yml} (100%) rename plugin/src/main/resources/messages/{french.yml => fr.yml} (100%) rename plugin/src/main/resources/messages/{portuguese.yml => pt.yml} (100%) rename plugin/src/main/resources/messages/{russian.yml => ru.yml} (100%) rename plugin/src/main/resources/messages/{chinese.yml => zh_cn.yml} (100%) diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 182af7da5..011766314 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -11,7 +11,8 @@ metrics: true update-checker: true # Language -lang: english +# https://github.com/Xiao-MoMi/Custom-Crops/tree/main/plugin/src/main/resources/messages +lang: en # World settings worlds: diff --git a/plugin/src/main/resources/messages/english.yml b/plugin/src/main/resources/messages/en.yml similarity index 100% rename from plugin/src/main/resources/messages/english.yml rename to plugin/src/main/resources/messages/en.yml diff --git a/plugin/src/main/resources/messages/spanish.yml b/plugin/src/main/resources/messages/es.yml similarity index 100% rename from plugin/src/main/resources/messages/spanish.yml rename to plugin/src/main/resources/messages/es.yml diff --git a/plugin/src/main/resources/messages/french.yml b/plugin/src/main/resources/messages/fr.yml similarity index 100% rename from plugin/src/main/resources/messages/french.yml rename to plugin/src/main/resources/messages/fr.yml diff --git a/plugin/src/main/resources/messages/portuguese.yml b/plugin/src/main/resources/messages/pt.yml similarity index 100% rename from plugin/src/main/resources/messages/portuguese.yml rename to plugin/src/main/resources/messages/pt.yml diff --git a/plugin/src/main/resources/messages/russian.yml b/plugin/src/main/resources/messages/ru.yml similarity index 100% rename from plugin/src/main/resources/messages/russian.yml rename to plugin/src/main/resources/messages/ru.yml diff --git a/plugin/src/main/resources/messages/chinese.yml b/plugin/src/main/resources/messages/zh_cn.yml similarity index 100% rename from plugin/src/main/resources/messages/chinese.yml rename to plugin/src/main/resources/messages/zh_cn.yml From f0bc600a7e27c289fdef735a5c3febbd85039c80 Mon Sep 17 00:00:00 2001 From: Schroddinger Date: Sun, 14 Apr 2024 11:06:36 -0500 Subject: [PATCH 014/329] Small tweaks to Spanish translation --- plugin/src/main/resources/messages/es.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/main/resources/messages/es.yml b/plugin/src/main/resources/messages/es.yml index d91d0c995..a638c3461 100644 --- a/plugin/src/main/resources/messages/es.yml +++ b/plugin/src/main/resources/messages/es.yml @@ -1,8 +1,8 @@ messages: prefix: '[CustomCrops] ' - reload: '¡Recargado! Tomó {time}ms.' + reload: '¡Recargado! Tardó {time}ms.' spring: 'Primavera' summer: 'Verano' autumn: 'Otoño' winter: 'Invierno' - no-season: 'LAS TEMPORADAS ESTAN DESACTIVADAS EN ESTE MUNDO' \ No newline at end of file + no-season: 'LAS ESTACIONES ESTÁN DESACTIVADAS EN ESTE MUNDO' \ No newline at end of file From 02414a290cf5135491d52fe0388e87a58076475b Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 15 Apr 2024 13:52:19 +0800 Subject: [PATCH 015/329] 3.4.4.2 --- .../customcrops/api/mechanic/world/BlockPos.java | 4 ++-- build.gradle.kts | 2 +- .../customcrops/mechanic/item/CustomProvider.java | 7 ++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/BlockPos.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/BlockPos.java index 319a2c7aa..10e29bc17 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/BlockPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/BlockPos.java @@ -52,7 +52,7 @@ public int getZ() { } public int getSectionID() { - return getY() / 16; + return (int) Math.floor((double) getY() / 16); } public int getY() { @@ -78,7 +78,7 @@ public int hashCode() { @Override public String toString() { - return "ChunkPos{" + + return "BlockPos{" + "x=" + getX() + "y=" + getY() + "z=" + getZ() + diff --git a/build.gradle.kts b/build.gradle.kts index 43c14fda6..b9822f7bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.4.1" + project.version = "3.4.4.2" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java index fe9188214..2b9a3417c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java @@ -26,10 +26,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.Player; +import org.bukkit.entity.*; import org.bukkit.inventory.ItemStack; import java.util.Collection; @@ -68,7 +65,7 @@ default boolean isAir(Location location) { return false; Location center = LocationUtils.toCenterLocation(location); Collection entities = center.getWorld().getNearbyEntities(center, 0.5,0.51,0.5); - entities.removeIf(entity -> entity instanceof Player); + entities.removeIf(entity -> (entity instanceof Player || entity instanceof Item)); return entities.size() == 0; } From 704ebf4ddb9454caacb37960543a9188a393cf27 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 15 Apr 2024 17:27:30 +0800 Subject: [PATCH 016/329] Fixed the bug of variation --- CustomCrops_3.4_Basic_Pack.zip | Bin 440613 -> 440610 bytes .../api/mechanic/world/SimpleLocation.java | 5 +---- gui-plugin/src/main/resources/config.yml | 20 ++++++++++++++++++ gui-plugin/src/main/resources/plugin.yml | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/CustomCrops_3.4_Basic_Pack.zip b/CustomCrops_3.4_Basic_Pack.zip index a257ca894128ff9216327af39dd008d3f935ec85..2533a4a0ffdfd7bbbeedacd1bf761264e7cb4ca4 100644 GIT binary patch delta 14745 zcmZvjc|c9u|Nfm_dza=(lqMOAR7e>bk@-{#i6&DL4KfdvF>`U$n@Ad!j(ZDar?nc*%=|5P(WFIoUZ=i#)NVZ4Vqd$d&v)<4KT1W`GV}D# zwqM4*S!w$v@{g+Kiq(zxoS*P`)NZTCrCqn?yxe!)bx6IL%S$$oDKXo!@TAY1a6W4N z`sg3i_j}Zyw@LZ8@u1O<`I8QxlAoR_eqj@o<>naIA)4N$o%*n$a-@r1YG2t}i{KL{ zy19h^5kEG_;A^u6+~^nDPZQ7AzJGCPZS3Bo_Sxyimj+z(`S|D6agSbB*vz`%ern;? zPu^wvpC%@x-DxrT&WCHEUt28Lb@_7CvIh%Fp5>=?Z|^nerTo8fN2a-r6MQ!PmHxBz zN?D74%)&fPFE?9K;JbFtnS!7HeJyw#7Gu3?nbiW*1tovZJJ&q)w#}11OH=)4UmooJ zWY*-K--o>#-CRw;Umu~&;5uNwX zu5U@j+k4iOyPxxMaP7Zo^~Jlr&HiZ8>iOek$5t99Ox+RqV#S<<&i#knJeru8aQE2Q z#HbFWYa?^CIvYf3_c#ct_{x_jLc*j70<9cv2@@nXOm;5bp%ze-t{Fw=N#!U6QI3*S&gX zIrd)Gm#YD_Gb3MqKK^)0_n)3Ee#Y(YoFep?k@h|Ova$qS?n@KX z-*HmqcJ23UHBPF3ZR^$RM($pm1Nk{~jj1A%*C&^TTK-y|Z?;ehzLfd5=3+VS>tED_ z*Y218@78H8B5pp4FSa`PV$8Op50~zboV@!+v*%+=CP!4{Y@IaX`urD5SFQZ-q0#Bc zS(on}w0~W_b4Kxz+riPhO?ur6KdQa>;PzHE$IEAh?Qn{kb9a31*ZC2Py+&Rf)G)oa z-&L#OWA1FcS1j+gyPZ|0JnzSi)|X;!BU*KOS-Hh(b6{~+r}SlqZcjgKe`ML9F7u{c z-4ro6aQSW1LEns8ht^EJ*lc>#fwRl|v@t-Grpl8 z>_4MmM|eg2xG!(D%=A}(D@s3<({Wfq*IUyjT{BNDn3;UDVcB2Z@)PzK4Kr>qyiduHs} z_1LBU9qm&~e}?U;xmDFE{M)TJ<=_3~+K2DwhK#XobScg>Cn49#yV;PX?Y}&Fv+Y&- zF`px5X*&b;t>e6%Z&_c<|6Jy}<;cXb2kH+h8TMDFGu(qs7XR7Ef6~+4zGXYk-Lv|! zjo!-uB- zb}#tP%=_I8I?lGPZta%#-K2Q>*6=^KEVj;z4+~awOsIJ*`P7jTWehtwj3+F^5etx>;3d5#!a{DIyE`?-GtuPLd};(4$mFs zapdau_Af`@jLf-MmEN*o!uCs53ofrcb}oP7zoA$Dtm=He`HORUSx0Ydy5{GK$ zh7&oD9yJ_j?%A`v`SA3M0lzmK8PvKoGi&|LtNvXEj{oxZaHrqzZOo}!Zj|+EOOKFTk9?BX)-0viDd4arO&hxbB<58lP!60KNcv{;O*!KH$ETR^yTljo@Ba+vo1?==i;9j z?ZqX!WN6}4c8R{UCI19%OR90u(kBN?Ed%oVN4q|`Me>4pxt8H_&DBbu968Z?Y|d7% zi7VfiM!NYpih4>5Cu2)sv9KY{&lMBUduN{7ya&`Hxzo&$+nR6zg z#g#XE`cDp9xn^PVpLV2k;M^l$t3PHgDqB_Pxm2Gk4@ymazC^KiNp#yO3wG5WO3ixk z+|+!PEbjMX*1OXC9xXc2i(NHwFVj_*&1?HS{ooVH(q*%mT{Z_lo*dJ9{6U{-d-kSW ztC}EQHQq0ZF73aaGc4Na{ij{GEWUP0eElim*~^^^QbV`>descFEvi54Mp$dv*-kP;bdDR&H+&c2%-jdYPPkSGvr+=NhPIuxlmkn-SZb$QbgzX%%`F(@4!`ChG z{c-&8*;jj8KYTWJUt!Ai;*~o>?yX7xI_GfTlBd}*lg>5IPi<2Z*~j$q!}sY{wqH~4 z{B}40;e*rXjZQ}oKYF*N>sy1|!=1YL&+*cpw&S3CP+nSn*HfjIXAf<7xB7GKv)h~h zcwKV)OE>DLqwQ!Nw`)U=kY}~ZI`XPv&^?d--Ed(f_cf^1-$4s?wA%^~%N0S>w$f1@ zZ4X%xy=5O~sFkj^jn_wq@UI6a_Z!~%)VsZ9p+7H9oDh+BwN+$%YWR$0S3c%FtWMdT z=xOjSXX6O%y93X6N%*w*_iEEW@*mJlU2R)0nPvHSoqs}g+PE3woO8uFSEV`UO5e{M z!OpoUx&H=<=iJk~H$Ly*^yR+toXf1X)hesD)oy;-@E5;9X0NqJTV^|$cw1)OmZ?66 zSsv(UnMKQAYq#OX>MBAU+tBmZ+H!6W`|d&G|HXH$`uOfoKiKye>3fZ{Oqc$R<_)O$ zjdlQcN=+@8%z%2;fOgaq%Q?jIt*DlP2tyH|umwGc<;|$@KhP_`Qh|KmYKL=6Of=fU z<}zb?U@sU@{dXv^#Y`2lqLO!Ll%2T-n0QEx6$ZZ>Ic;J(N-0*$Zan(B9}@;T6{!A?x2n+DjZDc@<+7g zToXV!jsK)Amvx}n&)S2ie>_Uqebx?;ZDOF5b8M6p1(`N7FoBX2(8{2% zXwRZp5Is0UUey~sMd@tjM1 zXKrx^O*l=)kKh)1sK5yB?@p@ky5#1qWlAPfQMRWRAHd!0tP1H+Mk}O^xrI?^sEyEx zE-a)-_7=!>8uYgr4DPCghE&erMjr5~3O1pv$r!W60$Nn)t5h1%@jJXNMd|PXvV1Di z;jeL?-Naf8r}O5N*{CdXP@* zrvm2my{*uY*3UqeHTno`4-!KTvrtRSOq^SZ2B7)DD(Xc+h5#EvRKP;vH$abU)6fNH zBPJc7j!<@nU_)BQXhKmaql$_!1FA6Qu&$h%QU zBgm|x7*G^OTat+tvRg(gk8S9U6`ExpBNnKhENe`qi&5J{Ym{mjtERbK(6{wV(Ax6G zh%_FqiZr2$SEx3}2G!b+0G&z>OE6fGwm2*HQlTw>gG-O2B3phgWlZMu=!gU4X7-S$ zje&eF+4u;i^pb&*@c_%1Le7mCtME^pOUs+^pd|yV%j^Is`#5!We;B2p~L8^gG zS%)Wpj;G3IydnAcqk8*hpnKFbk;|B_Qdm<=B+kr>&5vFldaF@s|5oVTb;@qVALnAz=%F(|kz7`x-byEw|4&K!=Hc?#hQQBak67rP1 zf~!w{8*p80YKLa-rs{V5UtHTIOhA}!OsPu;K7{*ysR(v#0KqI^_u=;alvNHglx2O#x#5!CeIBe~j>?7PC`v>wIW z#BldJg5iGM7pkeJlo4avX^dQF`T>49O(*31Vp`Up50#bB!~XnzSuj<|`TN|DON!l> zr%Mw-i(rh$s>=Y2sM-vLbwW_M;tIfgHX{ghDTEJ^*^$El{w6o_v10b)<&?h%!gZk# zy1u8~Vf=iG4C4pD^_7P4_qZMcJ&fTkDI=I4Ny+){PC}V`R>BWeDm!ZdNCd@m9}qZ=@6 zPdnH_p7OxfQf5G|Hn=c53`55sYH$PknE-r{VRuWZ=y*#^xSec)10sPl>tZY_4B?j9 zGNCgwiv|=D1x=Mm+B3^&Kvx-Yv|}1y$q|l-2AL(2p3FJQxoYtsR0`+EU~&ke>KOhv zZoDHa7s@PR827j4Ky-$dv~f5xxiyzG8f!Rc1Typ`|B?I~`!U03 zKnKP^cSa@?ga zL$VeKc2WSbDbQcp? z@2M!dVX%aMF>wL1t`swkuj1ZCO3~=HxMiN24*mTYiKv`f0IsZuGx)z`KiEe)xm4p; z7(5F~*=VLjqug-Eb4mmGq9R6oYcfekqrSzlJ}0QGm;qf*hc0W3iYg7+#thJf@ggOa zBeS5{vyo!G$vC?q03k%>Tj#t4(1P2BSG!JPeBud#;J}itI zpCl1@umo;QvO28sU%C{ZiJ~J?q!?V-ST1UUc%X7%*3@tjBtueFRP}f6QzPG zHx|YXnxr8rCl(+Jr-w`UJ6yyRDH!aCMM%+m`7Oh^{G-8`d3wR|9B01FayeoLW~hra zpzPOtL;8>5y)z|Tn8n9)G)sMenRbMh=-e#mw`{a4 zg6FDjJ<5tDUQ;EQ`8BkUq+y%E4qGaG&QKvC-bBaLC*Quz+$;r6` z`BKi{G%Z7l>xUH0i(#4EDW(`+{A8gNg_j%1?Z<;Ms~$5^6I!zgDX(mmi2X!uQ_0OG zZU*kRjaF>K1Sq;WrxLXDj!33Uv&}n3QnoYAbhVpnNY}TZUVk#(%3tM1>|)Ujm5-Bi zw@O7oCd|7H+y-SWWp9JB)qW`tEL=FZrA&Fi+}r&fXv&1c68RtZc4#MJ505C-zI-!M z>;lO@rdBI0+_+NUtK~|}EL^|cASJ(3$R2*0JFyq~ z^fMAp+lL2~!m|`x#!pwo955l{2#k2jen`sCi-s&k2;iRV(ST2k>pF$?qVxpCob3V|&T# zeP{Hk4$02p`bgI0tf}H8?pHJGOT=Ly-=0eUfaa5xNW_+i)^Fh{&?UASDmuTo)1WbS zj82zk7^gF6@duG)QpE>!FY*9>DZ@V@9NB~kGpX`V{FqfurGvsUoS3K1Vr)j`p5yPx zwCV0SK9*~TW$Wv3eNpGpzVXU~8Yj+xZZWb?LzIRu{sJo2c9u$s+eM<`dv}3v=J0&Z$tf3(1`mClrfMl)1sok_$=8qae8dap**`6h%ff+ei-UKVtl;k6+WR(UTu*J}V> zxy^e^w(IjPWGoU%cWEMAb{oBwQRQvEgi9aDq&?UoSv1#a5Ig8jCN3Bx*ep4h8!i%s zNq_h*o9`w~jxK+rkKvU3H(w~zC(}pxC}$u2u>NIDjUGdv8ZYW|H)|P@jXA2@{}|P^ z87ope(i&0CT+oOopid=A8UEV1>CZ!xe==N{AXO#cvz(FFKqurH;DP0e-FK zUsUNdRgD{y-*Tw)-a!4^3^leSqXF7@$Z$ko-z>-Y`PV=&e5NGG_7mLbF(ZxBC6b)Z zDHqU^|3d{cl(;e-JKZ(SSQ39Ev44x}PM2iw_yOv*Gy^*R7Tw#==w@#CLMdPMJ3f&! zUX)WLbRatgri1MFNEBSdho|r9y%C%S#78lu;eg!pLA=2bLEC*;-*_^*2 zG;^gG3T}`6nllWXR4|~3$<xf#=l@2G4+jwr4Ejybq{o>HaD*4-kwHtWS$gzmrCsGKIx#biJo4E@`BCRYCuyi34I&+6NFu*1vl{>Q_ zIfuZvVoAvfL%7GAr0-FjF6}CUY01`t;!%l6G3VHkn>K*!ZxUd3?JfhSwu|5wwr;t-FYJYyd&np$AoA>H(ZO`OkB77d}D5xC3y>qGB!Tq2cQL7p*UQo%?d z#q`2#xYPjT;R%UkSIYEM7ER7wI4M!LKR9FZi^N%a-4Hb$AyXrvnA17KqCI}`Wfo*- zjC5gtszRbUlQSbJb`q0_DP{FXn-ff+Yj{DzF)>0bvM>c%d`T@dCXHD;#$wzRj9Mp+ zSL0)9gxJYYt*MVhi$+V&Ho9r_C=u9SOU_*@?p_%UblDyDR zt7{#XR^AAy5)9Qbf7&qy!yIP?yxCZdyOLN6 zq;cCDFk(vw8VhpniFn||7Vj``=h_PGr3JfVHn?=A;yB*^*B1=x)${+wu3dnp6MMO! zp{2q6jls;PsXpuaGy^-qh&yRZvCdey^s__FT6WSwdNVB{HxaYsX*&!>Zw)nLo-Ld! zcTnp@yB5jCIo2U?YL1lYfWCKarp7kRsTq)Q6J(5Sp~hyksEH6IOQl#x_5{S7nw=y1 zd60ow9M_5|opM{jCI_|BG9kOBXy;!}jJnZ!c3|hs4m_BB!-8isq)$?knh;LhHN~Q~ zL~^fTrW@8A4g26C76K4wMS&hP7Y1;;?oukTU{VWV827a7z z&M|Vtt4?{kB%(5W7F5s%!qn~}{>AlKkhcp$ zmjk5|D(h!KcNp&3N5U%qXF=220>AI4N+g;{N06KROpIRT@lO4J}u~eJJfhp z8H+K+;#krnYz5A054<2uDuQX&h;%#P%NC2LO4JG$i)&Z&CNR&Mdo08lEx{^Vn zj_@EHEsG|q^$vU|O{Lfjti-5X=CT7a{lnywHYhlOVtYj5_Hl-H?EH`P_ZBK9URNf1`ksCH#ujSAdz3J8=h7ssIg`rRP=7T zv@QUd`e907nF>?29LS{`hT)@Gjf||CQm4FV>b!aa_n%q^!_^Ju>Q9r1#?giJ zLPfC}Y(>?*(Edi#nb1@6bg8|eNl+5e)XDmQoKh0e)fM+ag>Pm^rBt>~&MlZJk^ixD z)qSCUH%p3%o^D1z5U+HR{Ew+?&>xk3&XB~}{V~(+pRdx1w$6e2%TapmB1r;sH=3(n zEFG9k#v-n`0+$kmG@X{HvGSb_J{Wk75@!SpWa>iNOl)Ivu6ze>9L|tDRFPLZ76VnLc$euk*v=Kp%GnRcxjFrTTt6-%QN%i{e$2%2GBS11s zBx0g3xYHR%lDA4E<2Ywat9RgLS=bZvU&2tt25wWv4#BOd_Fyf@Xc%J6c4%UvUCbYb z6Vh;(nz~U~By?X&B@(<_t1Ljm@( zE=D7koBD zAhhF>lvFf`TNyc0DT&n%F=!;Hyjs#}EMg7AJ0D6|^oY5m(9idDWfb}uQY&R)i;UtT z-ow$5*NC*cw1_u84!7fADvg7KU-*g1L)fC76`dFZ>4`5A5lvuRJc#XgiNOC^k>Oa7 z1wYyR){MrFh5tKIk5ZEGqLyv~M(>mIpk^F?-*iU0$fPicxRxj9GBrfGZ@Xd~hVPaJ zPvkD?vQmz04NlG()Mp2+;OgXDu=u@$wEmWth;rxkS+s++^yZO-p-7?RBw+>D-B=2! zmfG;fNiurwK-tNHf^##Y|7PIv6Shsxr7916CkQ-cJEHAZQ;_Ai?J*C)u{}sdwy6>k9oyoG7_Uu@M9D9Xtr0bygwX!RbxPx{Hb*+b z@MfDjSmoX1+=eC+hHV?m4R2aUqjGEmxM?lxV3k>ub1hq`vC^j*j0tvpa|e2P{rfcx57(ugk2bOuIgcN<0m)w`b-w0{PE1wE>nA*|&_x{FcO zn`(F&VkWA+-9ajoGz<6U`JU1N_C4*%ISt_{SQAZ0O877=AA`soa?^#)vU~X3OZ6Ta z7E-D)ff#0>Kz3JFpeK6~EsE>!&GO-Z)==QLXaiA2cNWuTmz)NjrH1j385w!>z&(?`yL)DtN z#h`P0s!NEkwOP<+V3`j+Tr4b;t!5v-RJokj&+Uy@ZLpmxmo=f%rSRFJkDIg<;%f}# zbCdehZWd5&oEoqdJ6$@k3=*82%OKet!X&+5>+I>{a#SBNfB~O6-Yz!_a$~xZC1lCq z=dy(j-1}it8{zBzkAWMv0$K7SC4tJoS&;Ec;J_#ei!N>tBX6Q8gmCx8x*VpuXm;Qs z?wjsXVDy%Xz>}O;2_f9y%7dC7f&*P$g~In@nBJ3WCgb(`>_9vtjLtz@Z%|PVrmyA0 zrKtTfoW+NFBDy3O(OwixLKkWI&5$q+t!H307dn#M^Wi!1KAW7|C?0f`b^^*)qbc7c zqS+E1yavgdk$WDVxMGSh?|fk35CdztW_U{=ACGUE?N6t*XmA`}6JS{NQkyZISc}{( z5+p3X+BR$*gw@#J&lkL{Z3M2Fr^FjEGrw7&K2X}Q)tjJMyoicQ zRF8^HHlu^jMAEyC{W`N5!b?k-uy-BnHL3(-c$f-HaLyNJ(Zel5e=^R-1tjj2ZrXzA zVyfN(1CXMSqF8|o<-&5<0e)b%__p{rq;5hIsk``QL9MqTq`O86X;$DaF#Il0YPGmY zI&M2imvtifpT#(X9SF_CJIv@JLdu=e?TlVnFQw3|$c5|#_AQdIxFYufM7ERMcfs#E zZWhN+}L}FmEj(c!o!kS>96oE(>Y0^ zu~xx*q02fiVbNN>XQb+mRFZirY?nrHZ69a=Ox7sw`~wOp!#IntVVv5J#yVjSxJ(FA yFTdeUx4$JKTCE0U=xr_qmtk$Q=BX4A{Z-^aR5Q=RKP-P2MAbDDHc=|1S}XEC{}DJk(XVt8x^r) z)F^h13fN;ujT&3T8XMvJoSozD%==IHe*A^o$Lsl;nVs96d(Laub#EOuzIDhQrLt9f z#PnqvrxBav3KiZuSE$fABgnkwp0%peCdu2)uSIR2cVF|@wBUmg!Tw&O{2HXrc7Abd z>&P8n`xgEDVqE0jKTaKQ^t<=R<rVAvR`BMf zsYzm&cjJCYe${7G6Z4+e*ELPju{h|A2tCe;>AM zHSeqHb8*AaSBZ;yTx`|2(uHGZYV?}FEbac7yp({vCwZ|^d9mT`_KhC&>C3m5*FMEg zn0W7*e~Z+P8~@f6?w?fgM09bmM`@n5)NdGY*{{ipBFOMddC<-0V$cJCVx+Ex91YGCTSmGk;u8F^w* zmvNVOU&ueW>2b{#OICM36f>$0)O)>(@j);N5Tz8u3(C$9>R%VXO&N}C@A?(Uux8|0fzgVHosR_}8&J8*~Y)E0X zJ+tk9ZDVu%)4r%LyT^N_-HXm{e%-l7>!3Yjoj!dSFf(Oj<-b{YoSbtd zpHlL@r^ePT{IbH)eDI?)X=mc+r*90}7reOR2+u=#H$zQkNT%D4%s7@jY0m3~+g>() z_wd`RHj|sT|6$v}*wwpsz06G*Hl(0m#nigjCME6Jb$_<^;K%!?VNushWfS-P zu)U*2ZGR_+mL0DSfA6{3vrqqk!a;eLVrDm8bTH(_@27*>9!$F*HPqaB=kiWF&$qf6 z)HV9yF}Fzrod57xST@A;UF~~8)9<-9HvQKA;>GMH+JeSYf4x)v$n~cMji#=z*dtJ0 zWtJCn_*qd-y;*{*76Gx+iU%o!D=h4p9+Ya5j*mJ?Ay2JME z3}3tY$f_eB0t&*$&8QRKFL;Ogk8x{n+-@;hJM^C&N0#KdkE){i=j-MrGYi)g9D7)p zJhkkVbz;q;5Bp~v|0BO7u4>S)(aSH}yU$C=DgE^I`IDT&oEdAkU7CKn-=c2&yNnz5 z?<uCPDXwsZxbplN$OQ(0b z3<_ytXHh*gsAF@hk=ruOBgTw({w=My!|>?9n6W{vy~o{XVfBMa(s3uxtFQO?UHNfG za=(tT_q%+(owH-P=C6YH-V;iSvz|O&^2_Er)efC(cs^$Q)_%jTUd%n@v2s|Mw*BL! zUIC8nM;CY`UH&zobFF~n7Y%7cg9hGj!>PB4)U{Rn&@{Qv!bK4w9q-gA&79NH!Ym@a z-Hv?c_7ffkR(uq)@7ehmPfU|CdKa|u^3GXb_09EdLvDY{&8d=-JECu`OX=O3`xG^* z?qqFt;e4OyZVlH?bUV<%(QeZFg`RO+hu#~oA>H}H`??uE&d0rH*hQp9zIxQ+mgn4O zn!>_++S1ln&W#w+YyH@b7o%P^>T-YNtkg574&H5gq4ko5w()%$z25$yfqAOGP14lb zRMwPLZU1?;*R_4`D)jxf-zhi1DL22xRr%_XkptUW@3FeN-{r&BY0p0&x%_VC#@jZf z5&aH!uv|K=`Pr3YBA(`DJ|B7hK4te}4tcSjY{cVAD{!00a}M?7x5-~lWIK#Gl52kx znOtkgmK5+x?U2{N-Xzn6-z*mYceDK8&GP@{%`*G0lS$rPCw1*YtMAMhS-(<8S8eb% zr^2wrTdLxrZysJAdrS4~>!8x;M4I8GB@I>k zp!Q-()d2d_&JXHf=HjRb!#_g!$w_IuteL73c@&|kTK}M_09Q^kj=I{=g@15j=iM@n z)znhVCwy*M6Q4uL`ZGRn;-91V32RxcTKsdy+FBCrh(SZszo4Ow2H5bcI*jGk&hYIw zoJzhTp+y~hj-`8}ApZ3m#J|;{W2Ne`lobmyr4$p;lk!W|MXDpT_+Ry9R=Kf=yLQB2 zZQ^liU(0Z6-J1y9c)VO~=?Te?&1<1+Cqb%D8&r}Po7+;54)tUQier+W>JjZ?(lvI| zO9bu)s4QvVP4uf#qlM#b$<16ELzO%*+c!7~^5$eB<&J|UTLO8iBN4JRh-G&W;gX56 zGxe{C@ERXZQpm+r3Sn(JiUZe1a%HM$hVa$Sf)q^#`THlylpdQ&UaAG;YK~z|>>;9G z8q4;y&;sM#L6kGWLh1L-Aj zilyx3R<|#LGpWN2oL6Zo#%8I(*y{8Hokt~)(Z+oZ+Ncwj z;Tx7l>8&uqIWr+UW(QfV{-86+brzB{szT;EKmqw2gwKX-oIPgAiE{0wKbS)}?Xs6r z$>s+{U8+NFA0f!mXHsSQ#EB+S8LN+tY)du+Ow<(n978yohNkX0pvP)aJf8VNa-+SD z(AOBmk27)y+0mjjjBQB`K8nGBv6Sy3Sy0MR)MYR4I< zONnGpflH7uttJxON0)~zD1S1urQ}6uuvaY%(J4_0H6h80Y~0bz9zAfN?dh1d&+d>- zn^-P$pkzPL>$O1>CK+hpJ4|RwCb~RS2VF)@E)TV!Wpx39rexTjjG^}R(8rA_Wa}Y~ zrmPGM!>m3ojX`ANA&n!Cp8+m%@RoxMtZoWdj-`_ECZ-gyRMt>j12ooun!sgCRi@Pc zAKd3&4S{=17ua$>}q6YZ^m$bOwev zI&&?)zR)I6b(=|^p3-=-Zi+GAQ4Xe)&vFdnLsML1?Ww6Jt}&}QTs4XEyUR|rt{K|d zJy+z7?##@|dL?eqam}G`Lb=VQo9x7V5pBOpcBC3Dq0d_^NY)BwMfX;r{ns2{TPkp2 zU3Dee*-8ph^&(Yk=_FPr{28CQLsd~MHUVug*h$MMs)IC{u)b6xryrys)^mj*k$3Q= zv<|{BEWFTc!b$~a-UU7H1>Lh%3dl%VZ9(m~VKRPg3t7drBHQO4bT->@J*9gCduMYz zowV(wURZUr+er^t+9u9Y$@-AWoJL6)f9(!9->($eLAuR;-O8ggC|Ab$-tj>jZ?_4O z70oOtvLXg{#25HX4yGUT$XA_demJG@oeJ>jDD`K_yOhsZYph7S8D0F)2`L-*2-*pX zv-{|Qzce``W3s#i{WT z9+*Ye+vMuhzbm8x`GVwf(yAMzV=18;drg zv@$#_E%j)9Pk=4wIKWzBOL37n@9A2=pbLsBekW8>0RUwM6eX)BiiM(ItULJ8G#0->WEKEtVVEh zTK2#|G~CK*S)hx^f<{B|avJI!h4$mfbC47!2J<%uk2u)PJZl@t4Yn?LFuLne*C^&Q zRFb&E)3W^k!SHzwv}*_^ZK=oiDgBtH(f35=#;-bAMMHhC$@ejdU<51Eo@jKwxS3JR z;0cEeL+nWleaxM^LJheL=igW>j(c!#Xi0m9LwHsp!Q3HQQtuHUty^5|rD-G4cv~+{qq$$Sq$Z<~_?tpTa@VM3o!av7NDAzP#S*Sj%Pt#} z2yZMWqvL>mI~WPqUXvWD$7nQ`;it!@6yF)^GhC&X{oYZJtI$18OuOm{I!cfRGj%sX z;uCQ&Kr0S>#z@0hV1SX<;47PtR z!{2`f-hxF^r`DOnjZ1+u7a8Glcj(7vr0a1UOI%FZE2QDv6!>1ih#iueqM?5j>;6c+!kt8!E|gjHg74P z(CyUKnDPf46tRM3q}nLO!UAeppXL1XSnmFCZLCGVPjw`vo9s$kve2^sN}(xAXI1Dv zcZSK?z?xM8r!{BYa(%bdu<7Ac~iaR{D0q3?qbE8y* zjsB&L*xmKq#>26hYhc0aY=-`h093pSYm>6|Ea5nDyP^}g+!ET;l`ZJhFGs}k*UAlP z%T|yfJ9UJ~RVY3SKebbS0lt;1#|^31Hjt0I^`r__+AamN`uhYa+Aa-cTk~`W>*UsS zdj}H6925k@(-`@~{W&=b{gHr31n`BEsd<5dT>YU<^lfa*=b38|w z#F?jXQu8VQlyr|pIr9Ma%2p=ImeZ2zoWU3Rj{!Rvc|`NGXltmONQvwxHK!*a@M3P~ zu(yatJ&3mzUuxn)Sq_+>HNPV&(aqnbDAv3_?YaW1*#A5xBu;nmx<1oc#fRt7?LGt6 zS;goJz>x&pB=?=1rB$-NM@w1dvfbrZn;L4O`rZ%V20;rd^ z5UEgCChO61E!t9u`RdTh2-hf*2C%Aa^dyFbd+841V6N%J?UyT!nuuP#Zlkejp#o!}twY6}wCybjJXjc8)>k=z z{R(3r!$bhiD~!d5iv#Sj!=i9=d_Mea3CHvR_-ix;{V8=6TWXJoAYsD-jeRbqv%+=5l35{@q@xFZ$!GqIuMzi_ps#&Kas?#616%^b{qt7jOJ7bQHC z)~Vj`Kb`118>VGD1!#QZJ$J*uaW6mBK2iM zCX2qxOK=)`Py#7xm2afZVy7JY26xdE4mL6K*|h786vXRo4s_xzI?U6Nz<2nS95P3Q z3+FJI3Oiy@w(rrJEfu|&T9P9#tu1(u)}lC=quS5^cv0khR7R9C+lCJ~S(`Mv^j`Xb zQas>PI(|eS*7J0vi|j@>IkB2A2vV(hu}#a)>PXfE)Pt2eoBRn~&m!MX(gIa6|I;BO zVum%Zt!dfvg*?9nrDQQlDX#7QjGq72(UKupdK8c4_XW7yA|Z~CR#y|XwkKcE^muan zDy?JIi}g`kQE;pK4I`YoM2ClC3!D25eV)n?L}?xFY7m>V)QBkVs;m^LT~-KeGgggX zqgf`Fq5!w*U&I=((Z`%<(rV0byE5Q$YX!#nScui)WEnIz6r?j97OD{O@`roYfZQ`cd^5O@!rd5Mjw$+^uwST~ZkC%-pc%;U z1A;&}iiI8&2f16}?G0tTW#$n295#w69<99vaDyXy+=~9>q;kF>7?B%|`Vo^p*An>A zF+Hv!3Ve;NfD29-VWrA4c| zVf5ZqhC1wuUhQCTddsUIH2sl23 zwu5-?M?;7fos^uZNmbxWGG%u{1<_|QtEn#$YFShB|6-H~9cijPPce>hOPE;=T{pKd z$^bQXgompkE!N6NpxDA8)q%HI8(}5SP*1dUfB@Sj!wAb=qn0IT1h#30GRA=eawiIN zggVELV<+0>Tw+F;I2QYObgvj%ro84*^oL$^gI7(O24?F zjq!E4pgvjGg!+)~04CCwme)kjC|xPxawn;wCbf`-_lp$l)6Arz@RXZtLG9+D$Cl*k zjusOQq>AvE2izh2-58fw_H;(GUu|^oxskr5DufzeEqc$fg{K~C^ff-6`&?KDf`1JH z7hdhNq>}5Xa@DCTd#S!rR9!iX9ciPqk@N1(a)gDY=mFWHIvZ5s)h)S?_>z^p;RF}n+{=@yBJ|tf0OF8 z{VtY;KN=yWTQ{Sal9D?S8&Ns5}H-mtof9IVQuH%;U%EHvo* zD6C?y?}?qh)Tp6tp6K(OPGHkaj%Ia&b?WBWCoD=q-&dO9%D7>`W7*sgBTZRzoKo9R zLC{1sR0S$gR;pZ$4!1z-Z;e7qX+T)smI(dQ+mMA&B`VawcYk|~D}%K{d{7@lJd;-o zQf9)(Z9qs>ji$AhVG6kk?$R2dkb`VCtDhl7?|kv&)dtPA4Ks=vJg~VZ78iB(vk)lLV72yWz`28;qQu>*r~NglZ6IB zO$zTI2Q%$3eM&R(-eKD}=BfpQu zu?>me6Q!&f!UD$p7sH4Sr=Qvq}t%O2&nR84CNUddp{HM%W zEflc<%XR7wk|!PJ_Xu%!r>b^G_9```k(uOmv? z)4MM;XR(vu8bjIh4#)ekWl%6Ldrs~Ll8xO0h@tH18U~t%%>t;Q?0J&YF8L}e}U^gXJu|$?#yXypaWA@5(iLxBF@cGIu>ts4%)2LqYyQ zxglEaC5oFZqtQ+X#Yf`@>>WI_at-N=n=!*Mnqc^49X7VI)rO<3d7nf~aj{1@Ir+s% zU2b5t%(YbDa@(pQ^BAb-mhn}|onm6-sqAD0%1*?47I->tNwy==^;aE8P;avAbqqjms%EVAjZCu;?Q9M zygN^YTJe8K%edl(CSvC2Q&yrpgH6E!$YjNC`eCnZQG7|#TWXeyH zS2B-!LT_++c@v<&qTuFwgJ(%?e!}_JtIyR6);-HP%kc~yE;o2u_HP3r{9jw=hsT3K zlhCsjUU}5Dr5HB{(oROt!#o8x7`Nvf-)&)}Ry^C(DVV`IbZiRD?L@o+$=fQoYt`xa zR3z1EtH*ddBm~pmZVDVwR5Hd~Y=|`@^AxBvy!C2>hr@dj?F4q6hUwiV4l)x^qA#yd zYUu7Xq=&UvbV|s$RH>nf(-AiBU=-HXEPJtof|5^flWP1qu>&1Qg?O}&=o8qPx<7_{ z?KcCswy(fNEm4{H!pUtN$?ap}#9u_z(2AMBi~NKT*q)oX5Z0-qAn6@Z&bjjxE#8_1 z;b(!BL6;XYHT2VLgtm85GQ|1lDw=*QqO*Y-d|cifBvkHF9&)5g1Mox`uPJJ%|6GJF z=tFL_XdZ5S>$dRsWjr*?-;kvkyOnA9c6@(SD7hw~f~C|F>dymSsl!?Gf1zBJroKiEC}<&4wg%~O92?k6 zpKo|OQOhca>NI+@=9rE##u;!6I-QPN?H^Jt#>OIRpkD7liZ8QVg08xT8{u+GrlFlY zhW`QLnJG8r#!SoFNAQD|{K2!9Em97=xHHqT$GQWUGAFw9GZKpMet8vjE(Uq1?|O zMDOLe%z&%#A}MM$(MqPUZSR(&t-}U_o#YDGwz|WG2DYsp)me$*#Zc5rd9A8Gsa9c> zk4Ed;z$+{|Z>D8u#u#CPEnBo2otTePVz6Z)EJ}PfdJ?^14aR!Z7#p5M2dqVUpCr*V zd|EW~ouE6=S+ro5{F9zEq&nFc@L-C{mRGTG7*NcUuw)VI(9$NjP=&0M6Ih$+;^04S z?3eWrj!xAHxf|=T0p#5by;|qSUUB?(mcWV|Te=aQFQBMR@EA&k(;1&s&pBAlE}>$+ zQTEk4FfIEH{!<|^UzuC@@Zd86%DtDCEr8bqFxak6TOk_@tI6{e+w}xQ)rZ`E!3yk7 zBB1!JdfOnifUD$EgU>q0@kdxoj*AAOzB^(&NPD Date: Tue, 16 Apr 2024 21:25:58 +0800 Subject: [PATCH 017/329] update modules --- gui-plugin/.gitignore | 42 ------------------- gui-plugin/build.gradle.kts | 13 ------ .../customcrops/gui/CustomCropsGUI.java | 38 ----------------- .../customcrops/gui/GUIManager.java | 16 ------- gui-plugin/src/main/resources/config.yml | 20 --------- gui-plugin/src/main/resources/plugin.yml | 9 ---- settings.gradle | 1 - 7 files changed, 139 deletions(-) delete mode 100644 gui-plugin/.gitignore delete mode 100644 gui-plugin/build.gradle.kts delete mode 100644 gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java delete mode 100644 gui-plugin/src/main/java/net/momirealms/customcrops/gui/GUIManager.java delete mode 100644 gui-plugin/src/main/resources/config.yml delete mode 100644 gui-plugin/src/main/resources/plugin.yml diff --git a/gui-plugin/.gitignore b/gui-plugin/.gitignore deleted file mode 100644 index b63da4551..000000000 --- a/gui-plugin/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/gui-plugin/build.gradle.kts b/gui-plugin/build.gradle.kts deleted file mode 100644 index 7c655738c..000000000 --- a/gui-plugin/build.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -dependencies { - compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") - compileOnly(project(":api")) - implementation("xyz.xenondevs.invui:invui:1.27") { - exclude("org.jetbrains") - } -} - -tasks { - shadowJar { - relocate ("xyz.xenondevs", "net.momirealms.customcrops.libraries") - } -} \ No newline at end of file diff --git a/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java b/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java deleted file mode 100644 index b6f808c51..000000000 --- a/gui-plugin/src/main/java/net/momirealms/customcrops/gui/CustomCropsGUI.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.gui; - -import org.bukkit.plugin.java.JavaPlugin; - -public class CustomCropsGUI extends JavaPlugin { - - private static CustomCropsGUI instance; - - @Override - public void onEnable() { - instance = this; - } - - @Override - public void onDisable() { - } - - public static CustomCropsGUI getInstance() { - return instance; - } -} diff --git a/gui-plugin/src/main/java/net/momirealms/customcrops/gui/GUIManager.java b/gui-plugin/src/main/java/net/momirealms/customcrops/gui/GUIManager.java deleted file mode 100644 index 1ae4af480..000000000 --- a/gui-plugin/src/main/java/net/momirealms/customcrops/gui/GUIManager.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.momirealms.customcrops.gui; - -import net.momirealms.customcrops.api.event.PotInteractEvent; -import org.bukkit.Material; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; - -public class GUIManager implements Listener { - - @EventHandler (ignoreCancelled = true) - public void onInteractPot(PotInteractEvent event) { - if (event.getItemInHand().getType() != Material.AIR) - return; - - } -} diff --git a/gui-plugin/src/main/resources/config.yml b/gui-plugin/src/main/resources/config.yml deleted file mode 100644 index bd18ece9a..000000000 --- a/gui-plugin/src/main/resources/config.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Offset characters' unicodes -# Never edit this unless you know what you are doing -offset-characters: - font: customcrops:offset_chars - '1':  - '2':  - '4':  - '8':  - '16':  - '32':  - '64':  - '128':  - '-1':  - '-2':  - '-4':  - '-8':  - '-16':  - '-32':  - '-64':  - '-128':  \ No newline at end of file diff --git a/gui-plugin/src/main/resources/plugin.yml b/gui-plugin/src/main/resources/plugin.yml deleted file mode 100644 index 5972dc175..000000000 --- a/gui-plugin/src/main/resources/plugin.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: CustomCropsGUI -version: '${version}' -main: net.momirealms.customcrops.gui.CustomCropsGUI -api-version: 1.17 -load: POSTWORLD -authors: [ XiaoMoMi ] -folia-supported: true -depend: - - CustomCrops \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index a5b54b7a4..a492555fb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,5 +2,4 @@ rootProject.name = 'CustomCrops' include(":plugin") include(":api") include(":legacy-api") -include(":gui-plugin") From 8d3443f989181118aaee9a642be81e56047e8ad9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Wed, 17 Apr 2024 18:21:21 +0800 Subject: [PATCH 018/329] Fix possible compatibility breaks --- .../customcrops/api/CustomCropsPlugin.java | 2 ++ build.gradle.kts | 2 +- .../customcrops/CustomCropsPluginImpl.java | 5 ++++ .../compatibility/IntegrationManagerImpl.java | 25 +++++++++++-------- .../level/AureliumSkillsImpl.java | 8 +----- .../mechanic/item/ItemManagerImpl.java | 9 ++++++- plugin/src/main/resources/plugin.yml | 7 ------ 7 files changed, 32 insertions(+), 26 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java index 215f315e0..042c746d2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java +++ b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java @@ -120,4 +120,6 @@ public PlaceholderManager getPlaceholderManager() { public abstract void debug(String debug); public abstract void reload(); + + public abstract boolean doesHookedPluginExist(String plugin); } diff --git a/build.gradle.kts b/build.gradle.kts index b9822f7bb..c773d22c9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.4.2" + project.version = "3.4.4.3" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index 4953c70c1..afbc139a5 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -202,4 +202,9 @@ public HologramManager getHologramManager() { public boolean isHookedPluginEnabled(String plugin) { return Bukkit.getPluginManager().isPluginEnabled(plugin); } + + @Override + public boolean doesHookedPluginExist(String plugin) { + return Bukkit.getPluginManager().getPlugin(plugin) != null; + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java index 7c22e217e..02d585c09 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.compatibility; +import com.gamingmesh.jobs.Jobs; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.integration.LevelInterface; import net.momirealms.customcrops.api.integration.SeasonInterface; @@ -49,43 +50,47 @@ public IntegrationManagerImpl(CustomCropsPlugin plugin) { @Override public void init() { - if (plugin.isHookedPluginEnabled("MMOItems")) { + if (plugin.doesHookedPluginExist("MMOItems")) { plugin.getItemManager().registerItemLibrary(new MMOItemsItemImpl()); hookMessage("MMOItems"); } - if (plugin.isHookedPluginEnabled("Zaphkiel")) { + if (plugin.doesHookedPluginExist("Zaphkiel")) { plugin.getItemManager().registerItemLibrary(new ZaphkielItemImpl()); hookMessage("Zaphkiel"); } - if (plugin.isHookedPluginEnabled("NeigeItems")) { + if (plugin.doesHookedPluginExist("NeigeItems")) { plugin.getItemManager().registerItemLibrary(new NeigeItemsItemImpl()); hookMessage("NeigeItems"); } - if (plugin.isHookedPluginEnabled("MythicMobs")) { + if (plugin.doesHookedPluginExist("MythicMobs")) { plugin.getItemManager().registerItemLibrary(new MythicMobsItemImpl()); hookMessage("MythicMobs"); } - if (plugin.isHookedPluginEnabled("EcoJobs")) { + if (plugin.doesHookedPluginExist("EcoJobs")) { registerLevelPlugin("EcoJobs", new EcoJobsImpl()); hookMessage("EcoJobs"); } - if (plugin.isHookedPluginEnabled("AureliumSkills")) { + if (plugin.doesHookedPluginExist("Jobs")) { + registerLevelPlugin("JobsReborn", new JobsRebornImpl()); + hookMessage("JobsReborn"); + } + if (plugin.doesHookedPluginExist("AureliumSkills")) { registerLevelPlugin("AureliumSkills", new AureliumSkillsImpl()); hookMessage("AureliumSkills"); } - if (plugin.isHookedPluginEnabled("EcoSkills")) { + if (plugin.doesHookedPluginExist("EcoSkills")) { registerLevelPlugin("EcoSkills", new EcoSkillsImpl()); hookMessage("EcoSkills"); } - if (plugin.isHookedPluginEnabled("mcMMO")) { + if (plugin.doesHookedPluginExist("mcMMO")) { registerLevelPlugin("mcMMO", new McMMOImpl()); hookMessage("mcMMO"); } - if (plugin.isHookedPluginEnabled("MMOCore")) { + if (plugin.doesHookedPluginExist("MMOCore")) { registerLevelPlugin("MMOCore", new MMOCoreImpl()); hookMessage("MMOCore"); } - if (plugin.isHookedPluginEnabled("AuraSkills")) { + if (plugin.doesHookedPluginExist("AuraSkills")) { registerLevelPlugin("AuraSkills", new AuraSkillsImpl()); hookMessage("AuraSkills"); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java index 94f94a3b6..e81cc86c2 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java @@ -24,15 +24,9 @@ public class AureliumSkillsImpl implements LevelInterface { - private final Leveler leveler; - - public AureliumSkillsImpl() { - leveler = AureliumAPI.getPlugin().getLeveler(); - } - @Override public void addXp(Player player, String target, double amount) { - leveler.addXp(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target), amount); + AureliumAPI.getPlugin().getLeveler().addXp(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target), amount); } @Override diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 1a1428b35..589952877 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -236,7 +236,14 @@ public ItemStack getItemStack(Player player, String id) { return itemStack; if (id.contains(":")) { String[] split = id.split(":", 2); - return itemLibraryMap.get(split[0]).buildItem(player, split[1]); + ItemLibrary library = itemLibraryMap.get(split[0]); + if (library == null) { + LogUtils.warn("Error occurred when building item: " + id + ". Possible causes:"); + LogUtils.warn("① Item library: " + split[0] + " doesn't exist."); + LogUtils.warn("② If you are using ItemsAdder, " + id + " doesn't exist in your ItemsAdder config"); + return new ItemStack(Material.AIR); + } + return library.buildItem(player, split[1]); } else { try { return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 24cfed31f..6d3de5db1 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -12,16 +12,9 @@ softdepend: - ItemsAdder - Oraxen - PlaceholderAPI - - EcoJobs - BattlePass - BetonQuest - ClueScrolls - - mcMMO - - AureliumSkills - - AuraSkills - - MMOCore - - EcoSkills - - Jobs - RealisticSeasons - AdvancedSeasons - SlimeWorldManager From c602ba7c0e8e1346b8b14065a08598ad8e837dfa Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 20 Apr 2024 20:05:01 +0800 Subject: [PATCH 019/329] [Fix] Tripwire for sprinkler --- .../customcrops/api/common/Initable.java | 6 ++ .../customcrops/api/common/Property.java | 23 ----- .../api/common/item/EventItem.java | 6 ++ .../customcrops/api/common/item/KeyItem.java | 5 + .../api/event/BoneMealDispenseEvent.java | 15 +++ .../api/event/BoneMealUseEvent.java | 12 +++ .../customcrops/api/event/CropBreakEvent.java | 15 +++ .../api/event/CropInteractEvent.java | 3 + .../customcrops/api/event/CropPlantEvent.java | 1 + .../api/event/FertilizerUseEvent.java | 20 ++++ .../api/event/GreenhouseGlassBreakEvent.java | 6 ++ .../api/event/GreenhouseGlassPlaceEvent.java | 1 + .../customcrops/api/event/PotBreakEvent.java | 2 + .../customcrops/api/event/PotFillEvent.java | 12 +++ .../api/event/PotInteractEvent.java | 7 +- .../customcrops/api/event/PotPlaceEvent.java | 2 + .../api/event/ScarecrowBreakEvent.java | 1 + .../api/event/ScarecrowPlaceEvent.java | 1 + .../api/event/SeasonChangeEvent.java | 2 + .../api/event/SprinklerBreakEvent.java | 2 + .../api/event/SprinklerFillEvent.java | 7 ++ .../api/event/SprinklerInteractEvent.java | 3 + .../api/event/SprinklerPlaceEvent.java | 3 + .../api/event/WateringCanFillEvent.java | 20 ++++ .../api/event/WateringCanWaterEvent.java | 20 ++++ .../api/integration/ItemLibrary.java | 19 ++++ .../api/manager/ActionManager.java | 53 +++++++++++ .../api/manager/ConditionManager.java | 54 ++++++++++- .../api/manager/IntegrationManager.java | 11 +-- .../customcrops/api/manager/ItemManager.java | 12 +++ .../api/manager/RequirementManager.java | 59 +++++++++++- .../world/level/CustomCropsWorld.java | 2 + build.gradle.kts | 2 +- plugin/build.gradle.kts | 2 +- .../customcrops/CustomCropsPluginImpl.java | 14 ++- .../compatibility/IntegrationManagerImpl.java | 32 ------- .../level/AureliumSkillsImpl.java | 1 - .../compatibility/papi/CCPapi.java | 8 +- .../customcrops/manager/CommandManager.java | 26 +++++- .../condition/ConditionManagerImpl.java | 9 +- .../mechanic/item/ItemManagerImpl.java | 92 +++++++++++++++++-- .../item/custom/AbstractCustomListener.java | 10 +- .../requirement/RequirementManagerImpl.java | 5 - .../customcrops/mechanic/world/CWorld.java | 5 + 44 files changed, 503 insertions(+), 108 deletions(-) delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/common/Property.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/Initable.java b/api/src/main/java/net/momirealms/customcrops/api/common/Initable.java index 792ce4ea5..15ff32bb6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/common/Initable.java +++ b/api/src/main/java/net/momirealms/customcrops/api/common/Initable.java @@ -19,7 +19,13 @@ public interface Initable { + /** + * init + */ void init(); + /** + * disable + */ void disable(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/Property.java b/api/src/main/java/net/momirealms/customcrops/api/common/Property.java deleted file mode 100644 index f75361b2a..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/common/Property.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.common; - -public interface Property { - T get(); - void set(T value); -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java b/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java index 9910df0c6..8ef98ab96 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java @@ -22,6 +22,12 @@ public interface EventItem { + /** + * Trigger events + * + * @param actionTrigger trigger + * @param state state + */ void trigger(ActionTrigger actionTrigger, State state); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java b/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java index 18b344803..92d06af23 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java @@ -19,5 +19,10 @@ public interface KeyItem { + /** + * Get item's key + * + * @return key + */ String getKey(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java index 8d2ca6951..ed4ed5428 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java @@ -94,16 +94,31 @@ public ItemStack getBoneMealItem() { return boneMealItem; } + /** + * Get the crop's data + * + * @return crop data + */ @NotNull public WorldCrop getCrop() { return crop; } + /** + * Get the bone meal's config + * + * @return bone meal config + */ @NotNull public BoneMeal getBoneMeal() { return boneMeal; } + /** + * Get the dispenser block + * + * @return dispenser block + */ @NotNull public Block getDispenser() { return dispenser; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java index 2c0e4eff3..6216a37fc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java @@ -77,6 +77,7 @@ public HandlerList getHandlers() { /** * Get the crop location + * * @return location */ @NotNull @@ -87,6 +88,7 @@ public Location getLocation() { /** * Get the item in player's hand * If there's nothing in hand, it would return AIR + * * @return item in hand */ @NotNull @@ -94,11 +96,21 @@ public ItemStack getItemInHand() { return itemInHand; } + /** + * Get the crop's data + * + * @return crop data + */ @NotNull public WorldCrop getCrop() { return crop; } + /** + * Get the bone meal config + * + * @return bone meal config + */ @NotNull public BoneMeal getBoneMeal() { return boneMeal; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java index 80791f5a6..9a2bb76cb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java @@ -75,6 +75,7 @@ public HandlerList getHandlers() { /** * Get the crop's data, it might be null if it's spawned by other plugins in the wild + * * @return crop data */ @Nullable @@ -84,6 +85,7 @@ public WorldCrop getWorldCrop() { /** * Get the crop location + * * @return location */ @NotNull @@ -91,6 +93,14 @@ public Location getLocation() { return location; } + /** + * Get the entity that triggered the event + * This would be null if the crop is broken by respawn anchor + * The breaker can be a TNT, creeper. + * If the pot is a vanilla farmland, it can be trampled by entities + * + * @return entity + */ @Nullable public Entity getEntity() { return entity; @@ -104,6 +114,11 @@ public Player getPlayer() { return null; } + /** + * Get the reason + * + * @return reason + */ @NotNull public Reason getReason() { return reason; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java index 4f1fe413f..8e55b851c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java @@ -73,6 +73,7 @@ public HandlerList getHandlers() { /** * Get the crop location + * * @return location */ @NotNull @@ -83,6 +84,7 @@ public Location getLocation() { /** * Get the item in player's hand * If there's nothing in hand, it would return AIR + * * @return item in hand */ @NotNull @@ -92,6 +94,7 @@ public ItemStack getItemInHand() { /** * Get the crop's data, it might be null if it's spawned by other plugins in the wild + * * @return crop data */ @Nullable diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java index 2d55ab142..3b04f9dcd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java @@ -64,6 +64,7 @@ public void setCancelled(boolean cancel) { /** * Get the seed item + * * @return seed item */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java index 215479969..4376d9c09 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java @@ -74,21 +74,41 @@ public static HandlerList getHandlerList() { return handlers; } + /** + * Get the fertilizer item in hand + * + * @return item in hand + */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Get the pot's location + * + * @return location + */ @NotNull public Location getLocation() { return location; } + /** + * Get the pot's data + * + * @return pot data + */ @NotNull public WorldPot getPot() { return pot; } + /** + * Get the fertilizer's config + * + * @return fertilizer config + */ @NotNull public Fertilizer getFertilizer() { return fertilizer; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java index 1423b5413..9a5c8bfc3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java @@ -75,6 +75,7 @@ public HandlerList getHandlers() { /** * Get the glass location + * * @return location */ @NotNull @@ -100,6 +101,11 @@ public Reason getReason() { return reason; } + /** + * Get the glass data + * + * @return glass data + */ @NotNull public WorldGlass getGlass() { return glass; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java index 59cc4f6d0..f16133505 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java @@ -64,6 +64,7 @@ public HandlerList getHandlers() { /** * Get the glass location + * * @return location */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java index a0ea77ba5..a5cbb08fa 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java @@ -75,6 +75,7 @@ public HandlerList getHandlers() { /** * Get the pot location + * * @return location */ @NotNull @@ -85,6 +86,7 @@ public Location getLocation() { /** * Get the pot's data + * * @return pot */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java index 1319608f3..7a947e751 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java @@ -76,6 +76,7 @@ public HandlerList getHandlers() { /** * Get the pot location + * * @return location */ @NotNull @@ -86,6 +87,7 @@ public Location getLocation() { /** * Get the pot's data + * * @return pot */ @NotNull @@ -93,11 +95,21 @@ public WorldPot getPot() { return pot; } + /** + * Get the item in hand + * + * @return item in hand + */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Get the passive fill method + * + * @return passive fill method + */ @NotNull public PassiveFillMethod getFillMethod() { return fillMethod; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java index 52b947a1f..f1f5c6578 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java @@ -54,10 +54,6 @@ public boolean isCancelled() { return cancelled; } - /** - * Cancelling this event would cancel PotInfoEvent too - * @param cancel true if you wish to cancel this event - */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; @@ -77,6 +73,7 @@ public HandlerList getHandlers() { /** * Get the item in player's hand * If there's nothing in hand, it would return AIR + * * @return item in hand */ @NotNull @@ -86,6 +83,7 @@ public ItemStack getItemInHand() { /** * Get the pot location + * * @return pot location */ @NotNull @@ -95,6 +93,7 @@ public Location getLocation() { /** * Get the pot's data + * * @return pot key */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java index 875b1e3f7..4f8570f30 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java @@ -68,6 +68,7 @@ public HandlerList getHandlers() { /** * Get the pot location + * * @return location */ @NotNull @@ -77,6 +78,7 @@ public Location getLocation() { /** * Get the placed pot's config + * * @return pot */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java index 57e412dbf..d9a2a00ac 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java @@ -75,6 +75,7 @@ public HandlerList getHandlers() { /** * Get the scarecrow location + * * @return location */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java index b65a4bc6a..981aa8b37 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java @@ -64,6 +64,7 @@ public HandlerList getHandlers() { /** * Get the scarecrow location + * * @return location */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java index dfc46417e..6a67aa17e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java @@ -54,6 +54,7 @@ public HandlerList getHandlers() { /** * Get the new season + * * @return season */ @NotNull @@ -63,6 +64,7 @@ public Season getSeason() { /** * Get the world + * * @return world */ public World getWorld() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java index 586be8fee..34c797c94 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java @@ -75,6 +75,7 @@ public HandlerList getHandlers() { /** * Get the sprinkler location + * * @return location */ @NotNull @@ -84,6 +85,7 @@ public Location getLocation() { /** * Get the sprinkler's data + * * @return sprinkler */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java index 56db0f786..32a26a28a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java @@ -76,6 +76,7 @@ public HandlerList getHandlers() { /** * Get the item in player's hand + * * @return item in hand */ @NotNull @@ -85,6 +86,7 @@ public ItemStack getItemInHand() { /** * Get the sprinkler location + * * @return location */ @NotNull @@ -92,6 +94,11 @@ public Location getLocation() { return location; } + /** + * Get the passive fill method + * + * @return passive fill method + */ @NotNull public PassiveFillMethod getFillMethod() { return fillMethod; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java index f8ae29de9..d671fe3bf 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java @@ -72,6 +72,7 @@ public HandlerList getHandlers() { /** * Get the sprinkler location + * * @return location */ @NotNull @@ -81,6 +82,7 @@ public Location getLocation() { /** * Get the sprinkler's data + * * @return sprinkler */ @NotNull @@ -90,6 +92,7 @@ public WorldSprinkler getSprinkler() { /** * Get the item in player's hand + * * @return item in hand */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java index dec4943c3..d1469d8d2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java @@ -72,6 +72,7 @@ public HandlerList getHandlers() { /** * Get the item in player's hand + * * @return item in hand */ @NotNull @@ -81,6 +82,7 @@ public ItemStack getItemInHand() { /** * Get the sprinkler location + * * @return location */ @NotNull @@ -90,6 +92,7 @@ public Location getLocation() { /** * Get the sprinkler's config + * * @return sprinkler */ @NotNull diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java index 4275aeeac..9c4e3a353 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java @@ -74,21 +74,41 @@ public static HandlerList getHandlerList() { return handlers; } + /** + * Get the watering can item + * + * @return the watering can item + */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Get watering can config + * + * @return watering can config + */ @NotNull public WateringCan getWateringCan() { return wateringCan; } + /** + * Get the positive fill method + * + * @return positive fill method + */ @NotNull public PositiveFillMethod getFillMethod() { return fillMethod; } + /** + * Get the location + * + * @return location + */ @NotNull public Location getLocation() { return location; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterEvent.java index 37b8e30b2..20efeadd0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterEvent.java @@ -76,21 +76,41 @@ public static HandlerList getHandlerList() { return handlers; } + /** + * Get the watering can item + * + * @return watering can item + */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Get the watering can's config + * + * @return watering can config + */ @NotNull public WateringCan getWateringCan() { return wateringCan; } + /** + * Get the locations that involved in this event + * + * @return locations + */ @NotNull public Set getLocation() { return location; } + /** + * Get the pot/sprinkler's data + * + * @return data + */ @NotNull public CustomCropsBlock getPotOrSprinkler() { return potOrSprinkler; diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java b/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java index 0293d59c5..66a49b0b3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java @@ -22,9 +22,28 @@ public interface ItemLibrary { + /** + * Get the identification + * for instance "CustomItems" + * + * @return identification + */ String identification(); + /** + * Build an item instance for a player + * + * @param player player + * @param id id + * @return item + */ ItemStack buildItem(Player player, String id); + /** + * Get an item's id + * + * @param itemStack item + * @return ID + */ String getItemID(ItemStack itemStack); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java index 2e94c269d..47d4b34bc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java @@ -28,18 +28,71 @@ public interface ActionManager extends Reloadable { + /** + * Register a custom action type + * + * @param type type + * @param actionFactory action factory + * @return success or not + */ boolean registerAction(String type, ActionFactory actionFactory); + /** + * Unregister an action type by id + * + * @param type type + * @return success or not + */ boolean unregisterAction(String type); + /** + * Build an action instance with Bukkit configs + * + * @param section bukkit config + * @return action + */ Action getAction(ConfigurationSection section); + /** + * If an action type exists + * + * @param type type + * @return exist or not + */ + default boolean hasAction(String type) { + return getActionFactory(type) != null; + } + + /** + * Build an action map with Bukkit configs + * + * @param section bukkit config + * @return action map + */ HashMap getActionMap(ConfigurationSection section); + /** + * Build actions with Bukkit configs + * + * @param section bukkit config + * @return actions + */ Action[] getActions(ConfigurationSection section); + /** + * Get an action factory by type + * + * @param type type + * @return action factory + */ ActionFactory getActionFactory(String type); + /** + * Trigger actions + * + * @param state state + * @param actions actions + */ static void triggerActions(State state, Action... actions) { if (actions != null) for (Action action : actions) diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java index 59c0b70bb..91ce5a161 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java @@ -27,21 +27,73 @@ public interface ConditionManager extends Reloadable { + /** + * Register a custom condition type + * + * @param type type + * @param conditionFactory condition factory + * @return success or not + */ boolean registerCondition(String type, ConditionFactory conditionFactory); + /** + * Unregister a condition type by id + * + * @param type type + * @return success or not + */ boolean unregisterCondition(String type); - boolean hasCondition(String type); + /** + * If a condition type exists + * + * @param type type + * @return exist or not + */ + default boolean hasCondition(String type) { + return getConditionFactory(type) != null; + } + /** + * Build conditions with Bukkit configs + * + * @param section bukkit config + * @return conditions + */ @NotNull Condition[] getConditions(ConfigurationSection section); + /** + * Build a condition instance with Bukkit configs + * + * @param section bukkit config + * @return condition + */ Condition getCondition(ConfigurationSection section); + /** + * Build a condition instance with Bukkit configs + * + * @return condition + */ Condition getCondition(String key, Object args); + /** + * Get a condition factory by type + * + * @param type type + * @return condition factory + */ @Nullable ConditionFactory getConditionFactory(String type); + /** + * Are conditions met for a custom crops block + * + * @param block block + * @param offline is the check for offline ticks + * @param conditions conditions + * @return meet or not + */ static boolean isConditionMet(CustomCropsBlock block, boolean offline, Condition... conditions) { if (conditions == null) return true; for (Condition condition : conditions) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java index b983eb857..aa2e90fc1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java @@ -20,8 +20,6 @@ import net.momirealms.customcrops.api.common.Initable; import net.momirealms.customcrops.api.integration.LevelInterface; import net.momirealms.customcrops.api.integration.SeasonInterface; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import org.bukkit.World; import org.jetbrains.annotations.Nullable; public interface IntegrationManager extends Initable { @@ -51,9 +49,10 @@ public interface IntegrationManager extends Initable { */ @Nullable LevelInterface getLevelPlugin(String plugin); + /** + * Get the SeasonInterface provided by a plugin. + * + * @return the season interface + */ SeasonInterface getSeasonInterface(); - - Season getSeason(World world); - - int getDate(World world); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java index a1708dfc7..f57ae6bd7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java @@ -33,8 +33,20 @@ public interface ItemManager extends Reloadable { + /** + * Register an item library + * + * @param itemLibrary item library + * @return success or not + */ boolean registerItemLibrary(@NotNull ItemLibrary itemLibrary); + /** + * Unregister an item library by identification + * + * @param identification identification + * @return success or not + */ boolean unregisterItemLibrary(String identification); String getItemID(ItemStack itemStack); diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java index dc6eabe84..b4f6efb48 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java @@ -27,28 +27,81 @@ public interface RequirementManager extends Reloadable { + /** + * Register a custom requirement type + * + * @param type type + * @param requirementFactory requirement factory + * @return success or not + */ boolean registerRequirement(String type, RequirementFactory requirementFactory); + /** + * Unregister a custom requirement by type + * + * @param type type + * @return success or not + */ boolean unregisterRequirement(String type); + /** + * Build requirements with Bukkit configs + * + * @param section bukkit config + * @param advanced check "not-met-actions" or not + * @return requirements + */ @Nullable Requirement[] getRequirements(ConfigurationSection section, boolean advanced); - boolean hasRequirement(String type); + /** + * If a requirement type exists + * + * @param type type + * @return exist or not + */ + default boolean hasRequirement(String type) { + return getRequirementFactory(type) != null; + } + /** + * Build a requirement instance with Bukkit configs + * + * @param section bukkit config + * @param advanced check "not-met-actions" or not + * @return requirement + */ @NotNull Requirement getRequirement(ConfigurationSection section, boolean advanced); + /** + * Build a requirement instance with Bukkit configs + * + * @return requirement + */ @NotNull Requirement getRequirement(String type, Object value); + /** + * Get a requirement factory by type + * + * @param type type + * @return requirement factory + */ @Nullable RequirementFactory getRequirementFactory(String type); - static boolean isRequirementMet(State condition, Requirement... requirements) { + /** + * Are requirements met for a player + * + * @param state state + * @param requirements requirements + * @return meet or not + */ + static boolean isRequirementMet(State state, Requirement... requirements) { if (requirements == null) return true; for (Requirement requirement : requirements) { - if (!requirement.isStateMet(condition)) { + if (!requirement.isStateMet(state)) { return false; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java index ab70de72f..523908fb6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java @@ -68,6 +68,8 @@ public interface CustomCropsWorld { void unloadChunk(ChunkPos chunkPos); + void deleteChunk(ChunkPos chunkPos); + void setInfoData(WorldInfoData infoData); WorldInfoData getInfoData(); diff --git a/build.gradle.kts b/build.gradle.kts index c773d22c9..5e62fbfb4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.4.3" + project.version = "3.4.4.4" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 825bbbaf8..28e6696ea 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -51,7 +51,7 @@ dependencies { implementation(project(":legacy-api")) implementation("net.kyori:adventure-api:4.15.0") implementation("net.kyori:adventure-platform-bukkit:4.3.2") - implementation("com.github.Xiao-MoMi:AntiGriefLib:0.9.1") + implementation("com.github.Xiao-MoMi:AntiGriefLib:0.10") implementation("com.github.Xiao-MoMi:BiomeAPI:0.3") compileOnly("net.kyori:adventure-text-minimessage:4.15.0") diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index afbc139a5..3d7e1ede6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -52,7 +52,6 @@ public class CustomCropsPluginImpl extends CustomCropsPlugin { private PacketManager packetManager; private CommandManager commandManager; private HologramManager hologramManager; - private AntiGriefLib antiGriefLib; @Override public void onLoad() { @@ -71,11 +70,6 @@ public void onLoad() { Dependency.BSTATS_BUKKIT ) )); - - this.antiGriefLib = AntiGriefLib.builder(this) - .silentLogs(true) - .ignoreOP(true) - .build(); } @Override @@ -91,14 +85,18 @@ public void onEnable() { this.requirementManager = new RequirementManagerImpl(this); this.coolDownManager = new CoolDownManager(this); this.worldManager = new WorldManagerImpl(this); - this.itemManager = new ItemManagerImpl(this, antiGriefLib); + this.itemManager = new ItemManagerImpl(this, + AntiGriefLib.builder(this) + .silentLogs(true) + .ignoreOP(true) + .build() + ); this.messageManager = new MessageManagerImpl(this); this.packetManager = new PacketManager(this); this.commandManager = new CommandManager(this); this.placeholderManager = new PlaceholderManagerImpl(this); this.hologramManager = new HologramManager(this); this.commandManager.init(); - this.antiGriefLib.init(); this.integrationManager.init(); this.disableNBTAPILogs(); Migration.tryUpdating(); diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java index 02d585c09..1243e73ec 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java @@ -17,12 +17,10 @@ package net.momirealms.customcrops.compatibility; -import com.gamingmesh.jobs.Jobs; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.integration.LevelInterface; import net.momirealms.customcrops.api.integration.SeasonInterface; import net.momirealms.customcrops.api.manager.IntegrationManager; -import net.momirealms.customcrops.api.mechanic.world.season.Season; import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.compatibility.item.MMOItemsItemImpl; import net.momirealms.customcrops.compatibility.item.MythicMobsItemImpl; @@ -32,7 +30,6 @@ import net.momirealms.customcrops.compatibility.season.AdvancedSeasonsImpl; import net.momirealms.customcrops.compatibility.season.InBuiltSeason; import net.momirealms.customcrops.compatibility.season.RealisticSeasonsImpl; -import org.bukkit.World; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -110,13 +107,6 @@ public void disable() { } - /** - * Registers a level plugin with the specified name. - * - * @param plugin The name of the level plugin. - * @param level The implementation of the LevelInterface. - * @return true if the registration was successful, false if the plugin name is already registered. - */ @Override public boolean registerLevelPlugin(String plugin, LevelInterface level) { if (levelPluginMap.containsKey(plugin)) return false; @@ -124,12 +114,6 @@ public boolean registerLevelPlugin(String plugin, LevelInterface level) { return true; } - /** - * Unregisters a level plugin with the specified name. - * - * @param plugin The name of the level plugin to unregister. - * @return true if the unregistration was successful, false if the plugin name is not found. - */ @Override public boolean unregisterLevelPlugin(String plugin) { return levelPluginMap.remove(plugin) != null; @@ -139,12 +123,6 @@ private void hookMessage(String plugin) { LogUtils.info( plugin + " hooked!"); } - /** - * Get the LevelInterface provided by a plugin. - * - * @param plugin The name of the plugin providing the LevelInterface. - * @return The LevelInterface provided by the specified plugin, or null if the plugin is not registered. - */ @Override @Nullable public LevelInterface getLevelPlugin(String plugin) { @@ -155,14 +133,4 @@ public LevelInterface getLevelPlugin(String plugin) { public SeasonInterface getSeasonInterface() { return seasonInterface; } - - @Override - public Season getSeason(World world) { - return seasonInterface.getSeason(world); - } - - @Override - public int getDate(World world) { - return seasonInterface.getDate(world); - } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java index e81cc86c2..b9eb4bb43 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java @@ -18,7 +18,6 @@ package net.momirealms.customcrops.compatibility.level; import com.archyx.aureliumskills.api.AureliumAPI; -import com.archyx.aureliumskills.leveler.Leveler; import net.momirealms.customcrops.api.integration.LevelInterface; import org.bukkit.entity.Player; diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java index caff5e2cf..115084822 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java @@ -74,10 +74,10 @@ public boolean persist() { switch (split[0]) { case "season" -> { if (split.length == 1) { - return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeason(player.getWorld())); + return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(player.getWorld())); } else { try { - return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeason(Bukkit.getWorld(split[1]))); + return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(Bukkit.getWorld(split[1]))); } catch (NullPointerException e) { LogUtils.severe("World " + split[1] + " does not exist"); e.printStackTrace(); @@ -86,10 +86,10 @@ public boolean persist() { } case "date" -> { if (split.length == 1) { - return String.valueOf(plugin.getIntegrationManager().getDate(player.getWorld())); + return String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(player.getWorld())); } else { try { - return String.valueOf(plugin.getIntegrationManager().getDate(Bukkit.getWorld(split[1]))); + return String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(Bukkit.getWorld(split[1]))); } catch (NullPointerException e) { LogUtils.severe("World " + split[1] + " does not exist"); e.printStackTrace(); diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java index 67a71dd9f..807794436 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java @@ -24,9 +24,11 @@ import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.common.Initable; import net.momirealms.customcrops.api.integration.SeasonInterface; +import net.momirealms.customcrops.api.manager.AdventureManager; import net.momirealms.customcrops.api.manager.ConfigManager; import net.momirealms.customcrops.api.manager.MessageManager; import net.momirealms.customcrops.api.mechanic.item.ItemType; +import net.momirealms.customcrops.api.mechanic.world.ChunkPos; import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsSection; @@ -61,7 +63,8 @@ public void init() { getAboutCommand(), getSeasonCommand(), getDateCommand(), - getForceTickCommand() + getForceTickCommand(), + getUnsafeCommand() ) .register(); } @@ -81,6 +84,23 @@ private CommandAPICommand getReloadCommand() { }); } + private CommandAPICommand getUnsafeCommand() { + return new CommandAPICommand("unsafe") + .withSubcommands( + new CommandAPICommand("delete-chunk-data").executesPlayer((player, args) -> { + CustomCropsPlugin.get().getWorldManager().getCustomCropsWorld(player.getWorld()).ifPresent(customCropsWorld -> { + var optionalChunk = customCropsWorld.getLoadedChunkAt(ChunkPos.getByBukkitChunk(player.getChunk())); + if (optionalChunk.isEmpty()) { + AdventureManager.getInstance().sendMessageWithPrefix(player, "This chunk doesn't have any data."); + return; + } + customCropsWorld.deleteChunk(ChunkPos.getByBukkitChunk(player.getChunk())); + AdventureManager.getInstance().sendMessageWithPrefix(player, "Done."); + }); + }) + ); + } + private CommandAPICommand getAboutCommand() { return new CommandAPICommand("about").executes((sender, args) -> { plugin.getAdventure().sendMessage(sender, "<#FFA500>⛈ CustomCrops - <#87CEEB>" + CustomCropsPlugin.getInstance().getVersionManager().getPluginVersion()); @@ -138,7 +158,7 @@ private CommandAPICommand getDateCommand() { plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); return; } - plugin.getAdventure().sendMessageWithPrefix(sender, String.valueOf(plugin.getIntegrationManager().getDate(world))); + plugin.getAdventure().sendMessageWithPrefix(sender, String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(world))); }), new CommandAPICommand("set") .withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> plugin.getWorldManager().getCustomCropsWorlds().stream() @@ -196,7 +216,7 @@ private CommandAPICommand getSeasonCommand() { plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); return; } - plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeason(world))); + plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(world))); }), new CommandAPICommand("set") .withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java index 19356a7ef..597c8c0a9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java @@ -111,11 +111,6 @@ public boolean unregisterCondition(String type) { return this.conditionBuilderMap.remove(type) != null; } - @Override - public boolean hasCondition(String type) { - return conditionBuilderMap.containsKey(type); - } - @Override public @NotNull Condition[] getConditions(ConfigurationSection section) { ArrayList conditions = new ArrayList<>(); @@ -578,7 +573,7 @@ private void registerSeasonCondition() { registerCondition("suitable_season", (args) -> { HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); return (block, offline) -> { - Season season = plugin.getIntegrationManager().getSeason(block.getLocation().getBukkitWorld()); + Season season = plugin.getIntegrationManager().getSeasonInterface().getSeason(block.getLocation().getBukkitWorld()); if (season == null) { return true; } @@ -602,7 +597,7 @@ private void registerSeasonCondition() { registerCondition("unsuitable_season", (args) -> { HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); return (block, offline) -> { - Season season = plugin.getIntegrationManager().getSeason(block.getLocation().getBukkitWorld()); + Season season = plugin.getIntegrationManager().getSeasonInterface().getSeason(block.getLocation().getBukkitWorld()); if (season == null) { return false; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 589952877..1bc8b2a8c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -742,6 +742,79 @@ private void loadWateringCan(String key, ConfigurationSection section) { } this.registerItemFunction(itemID, FunctionTrigger.INTERACT_AT, + /* + * Handle clicking sprinkler with a watering can + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { + return FunctionResult.PASS; + } + // is a pot + Block block = blockWrapper.getClickedBlock(); + Sprinkler sprinkler = getSprinklerBy3DItemID(customProvider.getBlockID(block)); + if (sprinkler == null) { + return FunctionResult.PASS; + } + final Player player = blockWrapper.getPlayer(); + final ItemStack itemInHand = blockWrapper.getItemInHand(); + final Location clicked = block.getLocation(); + State state = new State(player, itemInHand, clicked); + // check watering-can requirements + if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { + return FunctionResult.RETURN; + } + // check whitelist + if (!wateringCan.getSprinklerWhitelist().contains(sprinkler.getKey())) { + wateringCan.trigger(ActionTrigger.WRONG_SPRINKLER, state); + return FunctionResult.RETURN; + } + // get water in can + int waterInCan = wateringCan.getCurrentWater(itemInHand); + + // check sprinkler requirements + if (!RequirementManager.isRequirementMet(state, sprinkler.getUseRequirements())) { + return FunctionResult.RETURN; + } + // check whitelist + if (!wateringCan.getSprinklerWhitelist().contains(sprinkler.getKey())) { + wateringCan.trigger(ActionTrigger.WRONG_SPRINKLER, state); + return FunctionResult.RETURN; + } + // check amount of water + if (waterInCan > 0 || wateringCan.isInfinite()) { + // get sprinkler data + SimpleLocation simpleLocation = SimpleLocation.of(clicked); + Optional worldSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation); + if (worldSprinkler.isEmpty()) { + plugin.debug("Player " + player.getName() + " tried to interact a sprinkler which not exists in memory. Fixing the data..."); + WorldSprinkler sp = new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0); + plugin.getWorldManager().addSprinklerAt(sp, simpleLocation); + worldSprinkler = Optional.of(sp); + } else { + if (sprinkler.getStorage() <= worldSprinkler.get().getWater()) { + return FunctionResult.RETURN; + } + } + + // fire the event + WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemInHand, new HashSet<>(Set.of(clicked)), wateringCan, worldSprinkler.get()); + if (EventUtils.fireAndCheckCancel(waterEvent)) + return FunctionResult.CANCEL_EVENT_AND_RETURN; + + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + state.setArg("{current}", String.valueOf(waterInCan - 1)); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); + wateringCan.updateItem(player, itemInHand, waterInCan - 1, state.getArgs()); + wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); + plugin.getWorldManager().addWaterToSprinkler(sprinkler, simpleLocation, 1); + } else { + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + state.setArg("{current}", "0"); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); + wateringCan.trigger(ActionTrigger.NO_WATER, state); + } + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.HIGH), /* * Handle clicking pot with a watering can */ @@ -893,6 +966,9 @@ private void loadWateringCan(String key, ConfigurationSection section) { } return FunctionResult.RETURN; }, CFunction.FunctionPriority.NORMAL), + /* + * Handle clicking crop with a watering can + */ new CFunction(conditionWrapper -> { if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { return FunctionResult.PASS; @@ -1316,12 +1392,12 @@ private void loadSprinkler(String key, ConfigurationSection section) { * Interact the sprinkler */ new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractFurnitureWrapper interactFurnitureWrapper)) { + if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { return FunctionResult.PASS; } - ItemStack itemInHand = interactFurnitureWrapper.getItemInHand(); - Player player = interactFurnitureWrapper.getPlayer(); - Location location = interactFurnitureWrapper.getLocation(); + ItemStack itemInHand = interactWrapper.getItemInHand(); + Player player = interactWrapper.getPlayer(); + Location location = interactWrapper.getLocation(); // check use requirements State state = new State(player, itemInHand, location); if (!RequirementManager.isRequirementMet(state, sprinkler.getUseRequirements())) { @@ -1381,6 +1457,7 @@ private void loadSprinkler(String key, ConfigurationSection section) { state.setArg("{current}", String.valueOf(sprinkler.getStorage())); state.setArg("{water_bar}", sprinkler.getWaterBar() == null ? "" : sprinkler.getWaterBar().getWaterBar(sprinkler.getStorage(), sprinkler.getStorage())); sprinkler.trigger(ActionTrigger.FULL, state); + return FunctionResult.CANCEL_EVENT_AND_RETURN; } } return FunctionResult.RETURN; @@ -1394,14 +1471,14 @@ private void loadSprinkler(String key, ConfigurationSection section) { this.registerItemFunction(new String[]{sprinkler.get3DItemID(), sprinkler.get3DItemWithWater()}, FunctionTrigger.BE_INTERACTED, new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractFurnitureWrapper interactFurnitureWrapper)) { + if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { return FunctionResult.PASS; } - Location location = interactFurnitureWrapper.getLocation(); + Location location = interactWrapper.getLocation(); // trigger interact actions plugin.getScheduler().runTaskSyncLater(() -> { - State state = new State(interactFurnitureWrapper.getPlayer(), interactFurnitureWrapper.getItemInHand(), location); + State state = new State(interactWrapper.getPlayer(), interactWrapper.getItemInHand(), location); Optional optionalSprinkler = plugin.getWorldManager().getSprinklerAt(SimpleLocation.of(location)); if (optionalSprinkler.isEmpty()) { return; @@ -2071,6 +2148,7 @@ private void loadPot(String key, ConfigurationSection section) { plugin.getWorldManager().addWaterToPot(pot, simpleLocation, method.getAmount()); } else { pot.trigger(ActionTrigger.FULL, state); + return FunctionResult.CANCEL_EVENT_AND_RETURN; } } return FunctionResult.RETURN; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java index 6a27fef06..5f20cb7fb 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java @@ -145,9 +145,15 @@ public void onBreakBlock(BlockBreakEvent event) { @EventHandler (ignoreCancelled = true) public void onPlaceBlock(BlockPlaceEvent event) { - onPlaceBlock( + final Block block = event.getBlock(); + // prevent players from placing blocks on entities (crops/sprinklers) + if (CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(block.getLocation())).isPresent()) { + event.setCancelled(true); + return; + } + this.onPlaceBlock( event.getPlayer(), - event.getBlock(), + block, event.getBlockPlaced().getType().name(), event ); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java index 1e0ee34c7..275616155 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java @@ -146,11 +146,6 @@ public Requirement[] getRequirements(ConfigurationSection section, boolean advan return requirements.toArray(new Requirement[0]); } - @Override - public boolean hasRequirement(String type) { - return requirementBuilderMap.containsKey(type); - } - @NotNull @Override public Requirement getRequirement(ConfigurationSection section, boolean advanced) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index a43884dab..92112962b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -276,6 +276,11 @@ public void unloadChunk(ChunkPos chunkPos) { } } + @Override + public void deleteChunk(ChunkPos chunkPos) { + CChunk chunk = loadedChunks.remove(chunkPos); + } + @Override public void setInfoData(WorldInfoData infoData) { this.infoData = infoData; From 484f4ec24d4e08af8f9cf86ea2df8fa793f34794 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 22 Apr 2024 01:43:04 +0800 Subject: [PATCH 020/329] [API] Added some API methods --- .../customcrops/api/CustomCropsPlugin.java | 1 + .../customcrops/api/manager/WorldManager.java | 23 ++++++++++++++++ .../mechanic/world/WorldManagerImpl.java | 26 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java index 042c746d2..b772c497d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java +++ b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api; import net.momirealms.customcrops.api.manager.*; +import net.momirealms.customcrops.api.mechanic.world.season.Season; import net.momirealms.customcrops.api.scheduler.Scheduler; import org.bukkit.plugin.java.JavaPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java index 7577a1945..8ddc5a1c6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java @@ -26,6 +26,7 @@ import org.bukkit.Chunk; import org.bukkit.World; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Optional; @@ -103,6 +104,28 @@ public interface WorldManager extends Reloadable { Optional getBlockAt(SimpleLocation location); + WorldCrop createCropData(SimpleLocation location, Crop crop, int point); + + default WorldCrop createCropData(SimpleLocation location, Crop crop) { + return createCropData(location, crop, 0); + } + + WorldSprinkler createSprinklerData(SimpleLocation location, Sprinkler sprinkler, int water); + + default WorldSprinkler createSprinklerData(SimpleLocation location, Sprinkler sprinkler) { + return createSprinklerData(location, sprinkler, 0); + } + + WorldPot createPotData(SimpleLocation location, Pot pot, int water, @Nullable Fertilizer fertilizer, int fertilizerTimes); + + default WorldPot createPotData(SimpleLocation location, Pot pot) { + return createPotData(location, pot, 0, null, 0); + } + + WorldGlass createGreenhouseGlassData(SimpleLocation location); + + WorldScarecrow createScarecrowData(SimpleLocation location); + void addWaterToSprinkler(@NotNull Sprinkler sprinkler, @NotNull SimpleLocation location, int amount); void addFertilizerToPot(@NotNull Pot pot, @NotNull Fertilizer fertilizer, @NotNull SimpleLocation location); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index e8d9ea268..5d0a6b779 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -29,6 +29,7 @@ import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.mechanic.world.adaptor.BukkitWorldAdaptor; import net.momirealms.customcrops.mechanic.world.adaptor.SlimeWorldAdaptor; +import net.momirealms.customcrops.mechanic.world.block.*; import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.Bukkit; import org.bukkit.Chunk; @@ -288,6 +289,31 @@ public Optional getBlockAt(SimpleLocation location) { return cWorld.getBlockAt(location); } + @Override + public WorldCrop createCropData(SimpleLocation location, Crop crop, int point) { + return new MemoryCrop(location, crop.getKey(), point); + } + + @Override + public WorldSprinkler createSprinklerData(SimpleLocation location, Sprinkler sprinkler, int water) { + return new MemorySprinkler(location, sprinkler.getKey(), water); + } + + @Override + public WorldPot createPotData(SimpleLocation location, Pot pot, int water, Fertilizer fertilizer, int fertilizerTimes) { + return new MemoryPot(location, pot.getKey(), water, fertilizer == null ? "" : fertilizer.getKey(), fertilizerTimes); + } + + @Override + public WorldGlass createGreenhouseGlassData(SimpleLocation location) { + return new MemoryGlass(location); + } + + @Override + public WorldScarecrow createScarecrowData(SimpleLocation location) { + return new MemoryScarecrow(location); + } + @Override public void addWaterToSprinkler(@NotNull Sprinkler sprinkler, @NotNull SimpleLocation location, int amount) { CWorld cWorld = loadedWorlds.get(location.getWorldName()); From 6de266a1e551eb06cf149f15c5574ac88d9bd8aa Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 22 Apr 2024 02:55:19 +0800 Subject: [PATCH 021/329] [API] Add more annotations --- .../customcrops/api/manager/ItemManager.java | 222 ++++++++++++++- .../customcrops/api/manager/WorldManager.java | 261 +++++++++++++++++- .../mechanic/item/ItemManagerImpl.java | 31 +-- .../item/custom/AbstractCustomListener.java | 2 +- .../customcrops/mechanic/world/CChunk.java | 2 + .../customcrops/mechanic/world/CWorld.java | 2 +- .../mechanic/world/WorldManagerImpl.java | 5 +- 7 files changed, 487 insertions(+), 38 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java index f57ae6bd7..4aa5db686 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java @@ -49,80 +49,294 @@ public interface ItemManager extends Reloadable { */ boolean unregisterItemLibrary(String identification); + /** + * Get item's id by ItemStack + * ItemsAdder: namespace:id + * Oraxen: id + * Vanilla: CAPITAL_ID + * Other item libraries: LibraryID:ItemID + * + * @param itemStack item + * @return ID + */ String getItemID(ItemStack itemStack); + /** + * Get item by ID + * ItemsAdder: namespace:id + * Oraxen: id + * Vanilla: CAPITAL_ID + * Other item libraries: LibraryID:ItemID + * + * @param player player + * @param id id + * @return item + */ ItemStack getItemStack(Player player, String id); + /** + * Place an item at a certain location + * + * @param location location + * @param carrier carrier + * @param id id + */ void placeItem(Location location, ItemCarrier carrier, String id); - void placeItem(Location location, ItemCarrier carrier, String id, CRotation rotate); + /** + * Place an item at a certain location + * + * @param location location + * @param carrier carrier + * @param id id + * @param rotation rotation + */ + void placeItem(Location location, ItemCarrier carrier, String id, CRotation rotation); + /** + * Remove any block/entity from a certain location + * + * @param location location + * @return the rotation of the removed entity + */ CRotation removeAnythingAt(Location location); + /** + * Get watering can config by ID + * + * @param id id + * @return watering can config + */ @Nullable WateringCan getWateringCanByID(@NotNull String id); + /** + * Get watering can config by item ID + * + * @param id item ID + * @return watering can config + */ @Nullable WateringCan getWateringCanByItemID(@NotNull String id); + /** + * Get watering can config by itemStack + * + * @param itemStack itemStack + * @return watering can config + */ @Nullable WateringCan getWateringCanByItemStack(@NotNull ItemStack itemStack); + /** + * Get sprinkler config by ID + * + * @param id id + * @return sprinkler config + */ @Nullable Sprinkler getSprinklerByID(@NotNull String id); + /** + * Get sprinkler config by 3D item ID + * + * @param id 3D item ID + * @return sprinkler config + */ @Nullable Sprinkler getSprinklerBy3DItemID(@NotNull String id); + /** + * Get sprinkler config by 2D item ID + * + * @param id 2D item ID + * @return sprinkler config + */ @Nullable Sprinkler getSprinklerBy2DItemID(@NotNull String id); + /** + * Get sprinkler config by entity + * + * @param entity entity + * @return sprinkler config + */ @Nullable Sprinkler getSprinklerByEntity(@NotNull Entity entity); + /** + * Get sprinkler config by block + * + * @param block block + * @return sprinkler config + */ @Nullable - Sprinkler getSprinklerBy2DItemStack(@NotNull ItemStack itemStack); + Sprinkler getSprinklerByBlock(@NotNull Block block); + /** + * Get sprinkler config by 2D itemStack + * + * @param itemStack 2D itemStack + * @return sprinkler config + */ @Nullable - Sprinkler getSprinklerBy3DItemStack(@NotNull ItemStack itemStack); + Sprinkler getSprinklerBy2DItemStack(@NotNull ItemStack itemStack); + /** + * Get sprinkler config by 3D itemStack + * + * @param itemStack 3D itemStack + * @return sprinkler config + */ @Nullable - Sprinkler getSprinklerByItemStack(@NotNull ItemStack itemStack); + Sprinkler getSprinklerBy3DItemStack(@NotNull ItemStack itemStack); + /** + * Get pot config by ID + * + * @param id id + * @return pot config + */ @Nullable Pot getPotByID(@NotNull String id); + /** + * Get pot config by block ID + * + * @param id block ID + * @return pot config + */ @Nullable Pot getPotByBlockID(@NotNull String id); + /** + * Get pot config by block + * + * @param block block + * @return pot config + */ @Nullable Pot getPotByBlock(@NotNull Block block); + /** + * Get pot config by block itemStack + * + * @param itemStack itemStack + * @return pot config + */ @Nullable Pot getPotByItemStack(@NotNull ItemStack itemStack); + /** + * Get fertilizer config by ID + * + * @param id id + * @return fertilizer config + */ + @Nullable Fertilizer getFertilizerByID(String id); + /** + * Get fertilizer config by item ID + * + * @param id item id + * @return fertilizer config + */ + @Nullable Fertilizer getFertilizerByItemID(String id); + /** + * Get fertilizer config by itemStack + * + * @param itemStack itemStack + * @return fertilizer config + */ + @Nullable Fertilizer getFertilizerByItemStack(@NotNull ItemStack itemStack); + /** + * Get crop config by ID + * + * @param id id + * @return crop config + */ + @Nullable Crop getCropByID(String id); + /** + * Get crop config by seed ID + * + * @param id seed ID + * @return crop config + */ + @Nullable Crop getCropBySeedID(String id); + /** + * Get crop config by seed itemStack + * + * @param itemStack seed itemStack + * @return crop config + */ + @Nullable Crop getCropBySeedItemStack(ItemStack itemStack); + /** + * Get crop config by stage item ID + * + * @param id stage item ID + * @return crop config + */ + @Nullable Crop getCropByStageID(String id); + /** + * Get crop config by entity + * + * @param entity entity + * @return crop config + */ + @Nullable Crop getCropByEntity(Entity entity); + /** + * Get crop config by block + * + * @param block block + * @return crop config + */ + @Nullable Crop getCropByBlock(Block block); + /** + * Get crop stage config by stage ID + * + * @param id stage ID + * @return crop stage config + */ + @Nullable Crop.Stage getCropStageByStageID(String id); + /** + * Update a pot's block state + * + * @param location location + * @param pot pot config + * @param hasWater has water or not + * @param fertilizer fertilizer + */ void updatePotState(Location location, Pot pot, boolean hasWater, Fertilizer fertilizer); + /** + * Get the pots that can be watered with a watering can + * + * @param baseLocation the clicked pot's location + * @param width width of the working range + * @param length length of the working range + * @param yaw player's yaw + * @param potID pot's ID + * @return the pots that can be watered + */ @NotNull Collection getPotInRange(Location baseLocation, int width, int length, float yaw, String potID); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java index 8ddc5a1c6..73133e642 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java @@ -83,90 +83,327 @@ public interface WorldManager extends Reloadable { @NotNull Collection getCustomCropsWorlds(); + /** + * Get CustomCrops world by name + * + * @param name name + * @return CustomCrops world + */ @NotNull Optional getCustomCropsWorld(@NotNull String name); + /** + * Get CustomCrops world by Bukkit world + * + * @param world world + * @return CustomCrops world + */ @NotNull Optional getCustomCropsWorld(@NotNull World world); + /** + * Get sprinkler at a certain location + * + * @param location location + * @return sprinkler + */ @NotNull Optional getSprinklerAt(@NotNull SimpleLocation location); + /** + * Get pot at a certain location + * + * @param location location + * @return pot + */ @NotNull Optional getPotAt(@NotNull SimpleLocation location); + /** + * Get crop at a certain location + * + * @param location location + * @return crop + */ @NotNull Optional getCropAt(@NotNull SimpleLocation location); + /** + * Get greenhouse glass at a certain location + * + * @param location location + * @return greenhouse glass + */ @NotNull Optional getGlassAt(@NotNull SimpleLocation location); + /** + * Get scarecrow at a certain location + * + * @param location location + * @return scarecrow + */ @NotNull Optional getScarecrowAt(@NotNull SimpleLocation location); + /** + * Get any CustomCrops block at a certain location + * The block can be crop, sprinkler and etc. + * + * @param location location + * @return CustomCrops block + */ Optional getBlockAt(SimpleLocation location); + /** + * Create crop data + * + * @param location location + * @param crop crop config + * @param point initial point + * @return the crop data + */ WorldCrop createCropData(SimpleLocation location, Crop crop, int point); + /** + * Create crop data + * + * @param location location + * @param crop crop config + * @return the crop data + */ default WorldCrop createCropData(SimpleLocation location, Crop crop) { return createCropData(location, crop, 0); } + /** + * Create sprinkler data + * + * @param location location + * @param sprinkler sprinkler config + * @param water initial water + * @return the sprinkler data + */ WorldSprinkler createSprinklerData(SimpleLocation location, Sprinkler sprinkler, int water); + /** + * Create sprinkler data + * + * @param location location + * @param sprinkler sprinkler config + * @return the sprinkler data + */ default WorldSprinkler createSprinklerData(SimpleLocation location, Sprinkler sprinkler) { return createSprinklerData(location, sprinkler, 0); } + /** + * Create pot data + * + * @param location location + * @param pot pot config + * @param water initial water + * @param fertilizer fertilizer config + * @param fertilizerTimes the remaining usages of the fertilizer + * @return the pot data + */ WorldPot createPotData(SimpleLocation location, Pot pot, int water, @Nullable Fertilizer fertilizer, int fertilizerTimes); + /** + * Create pot data + * + * @param location location + * @param pot pot config + * @return the pot data + */ default WorldPot createPotData(SimpleLocation location, Pot pot) { return createPotData(location, pot, 0, null, 0); } + /** + * Create Greenhouse glass data + * + * @param location location + * @return the greenhouse glass data + */ WorldGlass createGreenhouseGlassData(SimpleLocation location); + /** + * Create scarecrow data + * + * @param location location + * @return the scarecrow data + */ WorldScarecrow createScarecrowData(SimpleLocation location); + /** + * Add water to the sprinkler + * This method would create new sprinkler data if the sprinkler data not exists in that place + * This method would also update the sprinkler's model if it has models according to the water amount + * + * @param sprinkler sprinkler config + * @param location location + * @param amount amount of water + */ void addWaterToSprinkler(@NotNull Sprinkler sprinkler, @NotNull SimpleLocation location, int amount); + /** + * Add fertilizer to the pot + * This method would create new pot data if the pot data not exists in that place + * This method would update the pot's block state if it has appearance variations for different fertilizers + * + * @param pot pot config + * @param fertilizer fertilizer config + * @param location location + */ void addFertilizerToPot(@NotNull Pot pot, @NotNull Fertilizer fertilizer, @NotNull SimpleLocation location); - void addWaterToPot(@NotNull Pot pot, @NotNull SimpleLocation location, int amount); + /** + * Add water to the pot + * This method would create new pot data if the pot data not exists in that place + * This method would update the pot's block state if it's dry + * + * @param pot pot config + * @param amount amount of water + * @param location location + */ + void addWaterToPot(@NotNull Pot pot, int amount, @NotNull SimpleLocation location); + + /** + * Add points to a crop + * This method would do nothing if the crop data not exists in that place + * This method would change the stage of the crop and trigger the actions + * + * @param crop crop config + * @param points points to add + * @param location location + */ + void addPointToCrop(@NotNull Crop crop, int points, @NotNull SimpleLocation location); + /** + * Add greenhouse glass data + * + * @param glass glass data + * @param location location + */ void addGlassAt(@NotNull WorldGlass glass, @NotNull SimpleLocation location); + /** + * Add pot data + * + * @param pot pot data + * @param location location + */ + void addPotAt(@NotNull WorldPot pot, @NotNull SimpleLocation location); + + /** + * Add sprinkler data + * + * @param sprinkler sprinkler data + * @param location location + */ + void addSprinklerAt(@NotNull WorldSprinkler sprinkler, @NotNull SimpleLocation location); + + /** + * Add crop data + * + * @param crop crop data + * @param location location + */ + void addCropAt(@NotNull WorldCrop crop, @NotNull SimpleLocation location); + + /** + * Add scarecrow data + * + * @param scarecrow scarecrow data + * @param location location + */ void addScarecrowAt(@NotNull WorldScarecrow scarecrow, @NotNull SimpleLocation location); + /** + * Remove sprinkler data from a certain location + * + * @param location location + */ void removeSprinklerAt(@NotNull SimpleLocation location); + /** + * Remove pot data from a certain location + * + * @param location location + */ void removePotAt(@NotNull SimpleLocation location); + /** + * Remove crop data from a certain location + * + * @param location location + */ void removeCropAt(@NotNull SimpleLocation location); - boolean isReachLimit(SimpleLocation location, ItemType itemType); - - void addPotAt(@NotNull WorldPot pot, @NotNull SimpleLocation location); - - void addSprinklerAt(@NotNull WorldSprinkler sprinkler, @NotNull SimpleLocation location); + /** + * Remove greenhouse glass data from a certain location + * + * @param location location + */ + void removeGlassAt(@NotNull SimpleLocation location); - void addCropAt(@NotNull WorldCrop crop, @NotNull SimpleLocation location); + /** + * Remove scarecrow data from a certain location + * + * @param location location + */ + void removeScarecrowAt(@NotNull SimpleLocation location); - void addPointToCrop(@NotNull Crop crop, @NotNull SimpleLocation location, int points); + /** + * If a certain type of item reached the limitation + * + * @param location location + * @param itemType the type of the item + * @return reached or not + */ + boolean isReachLimit(SimpleLocation location, ItemType itemType); + /** + * Handle the load of a chunk + * It's recommended to call world.getChunkAt(x,z), otherwise you have to manually control the load/unload process + * + * @param bukkitChunk chunk + */ void handleChunkLoad(Chunk bukkitChunk); + /** + * Handle the unload of a chunk + * + * @param bukkitChunk chunk + */ void handleChunkUnload(Chunk bukkitChunk); + /** + * Save a chunk to region (from memory to memory) + * + * @param chunk the chunk to save + */ void saveChunkToCachedRegion(CustomCropsChunk chunk); + /** + * Save a region to file (from memory to disk) + * + * @param region the region to save + */ void saveRegionToFile(CustomCropsRegion region); - void removeGlassAt(@NotNull SimpleLocation location); - - void removeScarecrowAt(@NotNull SimpleLocation location); - CustomCropsBlock removeAnythingAt(SimpleLocation location); + /** + * Get the world adaptor + * + * @return the world adaptor + */ AbstractWorldAdaptor getWorldAdaptor(); + /** + * Save a world's season and date + * + * @param customCropsWorld the world to save + */ void saveInfoData(CustomCropsWorld customCropsWorld); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 1bc8b2a8c..f17ab16a1 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -409,6 +409,11 @@ public Sprinkler getSprinklerByEntity(@NotNull Entity entity) { return Optional.ofNullable(customProvider.getEntityID(entity)).map(threeDItem2SprinklerMap::get).orElse(null); } + @Override + public Sprinkler getSprinklerByBlock(@NotNull Block block) { + return Optional.ofNullable(customProvider.getBlockID(block)).map(threeDItem2SprinklerMap::get).orElse(null); + } + @Override public Sprinkler getSprinklerBy2DItemStack(@NotNull ItemStack itemStack) { return getSprinklerBy2DItemID(getItemID(itemStack)); @@ -439,13 +444,6 @@ public Pot getPotByItemStack(@NotNull ItemStack itemStack) { return getPotByBlockID(getItemID(itemStack)); } - @Override - public Sprinkler getSprinklerByItemStack(@NotNull ItemStack itemStack) { - if (itemStack.getType() == Material.AIR) - return null; - return getSprinklerBy2DItemID(getItemID(itemStack)); - } - @Override public Fertilizer getFertilizerByID(@NotNull String id) { return id2FertilizerMap.get(id); @@ -566,7 +564,7 @@ private void loadDeadCrops() { } method.trigger(potState); pot.trigger(ActionTrigger.ADD_WATER, potState); - plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(potLocation), method.getAmount()); + plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), SimpleLocation.of(potLocation)); } else { pot.trigger(ActionTrigger.FULL, potState); } @@ -607,7 +605,7 @@ private void loadDeadCrops() { } method.trigger(potState); pot.trigger(ActionTrigger.ADD_WATER, potState); - plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(potLocation), method.getAmount()); + plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), SimpleLocation.of(potLocation)); } else { pot.trigger(ActionTrigger.FULL, potState); } @@ -873,7 +871,7 @@ private void loadWateringCan(String key, ConfigurationSection section) { wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); for (Location location : waterEvent.getLocation()) { - plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater()); + plugin.getWorldManager().addWaterToPot(pot, wateringCan.getWater(), SimpleLocation.of(location)); pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); } } else { @@ -955,7 +953,7 @@ private void loadWateringCan(String key, ConfigurationSection section) { wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); for (Location location : waterEvent.getLocation()) { - plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater()); + plugin.getWorldManager().addWaterToPot(pot, wateringCan.getWater(), SimpleLocation.of(location)); pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); } } else { @@ -1037,7 +1035,7 @@ private void loadWateringCan(String key, ConfigurationSection section) { wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); for (Location location : waterEvent.getLocation()) { - plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater()); + plugin.getWorldManager().addWaterToPot(pot, wateringCan.getWater(), SimpleLocation.of(location)); pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); } } else { @@ -1655,7 +1653,6 @@ private void loadFertilizer(String key, ConfigurationSection section) { // add data plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); - updatePotState(location, pot, hasWater, fertilizer); if (interactBlockWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { itemInHand.setAmount(itemInHand.getAmount() - 1); } @@ -1723,7 +1720,6 @@ private void loadFertilizer(String key, ConfigurationSection section) { // add data plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); - updatePotState(potLocation, pot, hasWater, fertilizer); if (interactBlockWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { itemInHand.setAmount(itemInHand.getAmount() - 1); } @@ -1783,7 +1779,6 @@ private void loadFertilizer(String key, ConfigurationSection section) { // add data plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); - updatePotState(potLocation, pot, hasWater, fertilizer); if (furnitureWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { itemInHand.setAmount(itemInHand.getAmount() - 1); } @@ -1962,7 +1957,7 @@ private void loadCrop(String key, ConfigurationSection section) { } method.trigger(potState); pot.trigger(ActionTrigger.ADD_WATER, potState); - plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(potLocation), method.getAmount()); + plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), SimpleLocation.of(potLocation)); } else { pot.trigger(ActionTrigger.FULL, potState); } @@ -1989,7 +1984,7 @@ private void loadCrop(String key, ConfigurationSection section) { } } boneMeal.trigger(cropState); - plugin.getWorldManager().addPointToCrop(crop, SimpleLocation.of(cropLocation), boneMeal.getPoint()); + plugin.getWorldManager().addPointToCrop(crop, boneMeal.getPoint(), SimpleLocation.of(cropLocation)); return FunctionResult.RETURN; } } @@ -2145,7 +2140,7 @@ private void loadPot(String key, ConfigurationSection section) { } method.trigger(state); pot.trigger(ActionTrigger.ADD_WATER, state); - plugin.getWorldManager().addWaterToPot(pot, simpleLocation, method.getAmount()); + plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), simpleLocation); } else { pot.trigger(ActionTrigger.FULL, state); return FunctionResult.CANCEL_EVENT_AND_RETURN; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java index 5f20cb7fb..76f25842c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java @@ -294,7 +294,7 @@ public void onDispenser(BlockDispenseEvent event) { if (id.equals(itemID)) { storage.setAmount(storage.getAmount() - 1); boneMeal.trigger(new State(null, itemStack, location)); - CustomCropsPlugin.get().getWorldManager().addPointToCrop(config, simpleLocation, boneMeal.getPoint()); + CustomCropsPlugin.get().getWorldManager().addPointToCrop(config, boneMeal.getPoint(), simpleLocation); } } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java index a5861ea2f..d8470efa5 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -306,8 +306,10 @@ public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation lo memoryPot.setFertilizer(fertilizer); addBlockAt(memoryPot, location); CustomCropsPlugin.get().debug("When adding fertilizer to pot at " + location + ", the pot data doesn't exist."); + CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, false, fertilizer); } else { optionalWorldPot.get().setFertilizer(fertilizer); + CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, optionalWorldPot.get().getWater() > 0, fertilizer); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index 92112962b..a944ca91e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -424,7 +424,7 @@ public void addPointToCrop(Crop crop, SimpleLocation location, int points) { if (chunk.isPresent()) { chunk.get().addPointToCrop(crop, location, points); } else { - LogUtils.warn("Invalid operation: Adding points to crop in a not generated chunk"); + LogUtils.warn("Invalid operation: Adding point to crop in a not generated chunk"); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index 5d0a6b779..32952db1d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -335,7 +335,8 @@ public void addFertilizerToPot(@NotNull Pot pot, @NotNull Fertilizer fertilizer, } @Override - public void addWaterToPot(@NotNull Pot pot, @NotNull SimpleLocation location, int amount) { + public void addWaterToPot(@NotNull Pot pot, int amount, @NotNull SimpleLocation location) { + if (amount <= 0) return; CWorld cWorld = loadedWorlds.get(location.getWorldName()); if (cWorld == null) { LogUtils.warn("Unsupported operation: Adding water to pot in unloaded world " + location); @@ -375,7 +376,7 @@ public void addCropAt(@NotNull WorldCrop crop, @NotNull SimpleLocation location) } @Override - public void addPointToCrop(@NotNull Crop crop, @NotNull SimpleLocation location, int points) { + public void addPointToCrop(@NotNull Crop crop, int points, @NotNull SimpleLocation location) { CWorld cWorld = loadedWorlds.get(location.getWorldName()); if (cWorld == null) { LogUtils.warn("Unsupported operation: Adding point to crop in unloaded world " + location); From 6dd75c6c404a1c839d46009e3c8e94038e647497 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 22 Apr 2024 04:49:48 +0800 Subject: [PATCH 022/329] [API] Improved API --- .../api/common/item/EventItem.java | 1 - .../customcrops/api/manager/ItemManager.java | 8 + .../customcrops/api/manager/WorldManager.java | 21 +- .../api/mechanic/action/Action.java | 7 +- .../api/mechanic/action/ActionExpansion.java | 20 ++ .../api/mechanic/action/ActionFactory.java | 7 + .../api/mechanic/condition/Condition.java | 7 + .../condition/ConditionExpansion.java | 20 ++ .../mechanic/condition/ConditionFactory.java | 6 + .../api/mechanic/condition/Conditions.java | 5 + .../mechanic/condition/DeathConditions.java | 17 + .../api/mechanic/item/BoneMeal.java | 35 ++ .../customcrops/api/mechanic/item/Crop.java | 128 +++++++- .../api/mechanic/item/Fertilizer.java | 39 +++ .../customcrops/api/mechanic/item/Pot.java | 76 +++++ .../api/mechanic/item/Scarecrow.java | 5 + .../api/mechanic/item/Sprinkler.java | 74 +++++ .../api/mechanic/item/WateringCan.java | 74 +++++ .../mechanic/item/fertilizer/QualityCrop.java | 11 + .../mechanic/item/fertilizer/SoilRetain.java | 5 + .../mechanic/item/fertilizer/SpeedGrow.java | 5 + .../mechanic/item/fertilizer/Variation.java | 5 + .../item/fertilizer/YieldIncrease.java | 5 + .../item/water/PassiveFillMethod.java | 20 ++ .../item/water/PositiveFillMethod.java | 7 +- .../api/mechanic/misc/CRotation.java | 37 +++ .../customcrops/api/mechanic/misc/Value.java | 6 + .../api/mechanic/requirement/Requirement.java | 6 + .../requirement/RequirementExpansion.java | 20 ++ .../requirement/RequirementFactory.java | 14 + .../api/mechanic/world/CustomCropsBlock.java | 5 + .../api/mechanic/world/Tickable.java | 6 + .../world/level/AbstractCustomCropsBlock.java | 33 +- .../world/level/CustomCropsChunk.java | 270 ++++++++++++++-- .../world/level/CustomCropsRegion.java | 35 ++ .../world/level/CustomCropsSection.java | 41 +++ .../world/level/CustomCropsWorld.java | 305 +++++++++++++++++- .../api/mechanic/world/level/DataBlock.java | 22 ++ .../api/mechanic/world/level/WorldCrop.java | 20 ++ .../mechanic/world/level/WorldInfoData.java | 21 ++ .../api/mechanic/world/level/WorldPot.java | 53 ++- .../mechanic/world/level/WorldSprinkler.java | 20 ++ build.gradle.kts | 2 +- plugin/build.gradle.kts | 2 +- .../mechanic/item/CustomProvider.java | 25 +- .../mechanic/item/ItemManagerImpl.java | 11 +- .../mechanic/item/impl/CropConfig.java | 2 + .../customcrops/mechanic/world/CChunk.java | 48 ++- .../customcrops/mechanic/world/CWorld.java | 51 +-- .../mechanic/world/WorldManagerImpl.java | 36 +-- .../mechanic/world/block/MemoryPot.java | 5 +- .../customcrops/util/DisplayEntityUtils.java | 2 +- .../customcrops/util/RotationUtils.java | 35 -- 53 files changed, 1593 insertions(+), 148 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java b/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java index 8ef98ab96..c6a3ad053 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java @@ -29,5 +29,4 @@ public interface EventItem { * @param state state */ void trigger(ActionTrigger actionTrigger, State state); - } diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java index 4aa5db686..d0e7d16bd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java @@ -101,6 +101,14 @@ public interface ItemManager extends Reloadable { */ CRotation removeAnythingAt(Location location); + /** + * Get the rotation of the removed entity + * + * @param location location + * @return rotation + */ + CRotation getRotation(Location location); + /** * Get watering can config by ID * diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java index 73133e642..edff0c586 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java @@ -323,35 +323,40 @@ default WorldPot createPotData(SimpleLocation location, Pot pot) { * * @param location location */ - void removeSprinklerAt(@NotNull SimpleLocation location); + @Nullable + WorldSprinkler removeSprinklerAt(@NotNull SimpleLocation location); /** * Remove pot data from a certain location * * @param location location */ - void removePotAt(@NotNull SimpleLocation location); + @Nullable + WorldPot removePotAt(@NotNull SimpleLocation location); /** * Remove crop data from a certain location * * @param location location */ - void removeCropAt(@NotNull SimpleLocation location); + @Nullable + WorldCrop removeCropAt(@NotNull SimpleLocation location); /** * Remove greenhouse glass data from a certain location * * @param location location */ - void removeGlassAt(@NotNull SimpleLocation location); + @Nullable + WorldGlass removeGlassAt(@NotNull SimpleLocation location); /** * Remove scarecrow data from a certain location * * @param location location */ - void removeScarecrowAt(@NotNull SimpleLocation location); + @Nullable + WorldScarecrow removeScarecrowAt(@NotNull SimpleLocation location); /** * If a certain type of item reached the limitation @@ -391,6 +396,12 @@ default WorldPot createPotData(SimpleLocation location, Pot pot) { */ void saveRegionToFile(CustomCropsRegion region); + /** + * Remove any block data from a certain location + * + * @param location location + * @return block data + */ CustomCropsBlock removeAnythingAt(SimpleLocation location); /** diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java index 860b7b5c6..fde774efe 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java @@ -21,5 +21,10 @@ public interface Action { - void trigger(State condition); + /** + * Trigger the action + * + * @param state the state of the player + */ + void trigger(State state); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java index e349c7c44..9f9f9a3dc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java @@ -19,11 +19,31 @@ public abstract class ActionExpansion { + /** + * Get the version number + * + * @return version + */ public abstract String getVersion(); + /** + * Get the author + * + * @return author + */ public abstract String getAuthor(); + /** + * Get the type of the action + * + * @return the type of the action + */ public abstract String getActionType(); + /** + * Get the action factory + * + * @return the action factory + */ public abstract ActionFactory getActionFactory(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java index 61fa1368a..47389e484 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java @@ -19,5 +19,12 @@ public interface ActionFactory { + /** + * Build an action by args and chance + * + * @param args args + * @param chance chance (0-1) + * @return action + */ Action build(Object args, double chance); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java index fc9a96f38..1f38f85e8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java @@ -21,5 +21,12 @@ public interface Condition { + /** + * If the block meets the conditions + * + * @param block block + * @param offline offline tick + * @return met or not + */ boolean isConditionMet(CustomCropsBlock block, boolean offline); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java index 1f8691c92..0ecde8a5c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java @@ -19,11 +19,31 @@ public abstract class ConditionExpansion { + /** + * Get the version number + * + * @return version + */ public abstract String getVersion(); + /** + * Get the author + * + * @return author + */ public abstract String getAuthor(); + /** + * Get the type of the condition + * + * @return the type of the condition + */ public abstract String getConditionType(); + /** + * Get the condition factory + * + * @return the condition factory + */ public abstract ConditionFactory getConditionFactory(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java index bde4c2a95..97beecea6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java @@ -19,5 +19,11 @@ public interface ConditionFactory { + /** + * Build a condition with the args + * + * @param args args + * @return condition + */ Condition build(Object args); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java index e0f7c9a5b..aa38313ba 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java @@ -8,6 +8,11 @@ public Conditions(Condition[] conditions) { this.conditions = conditions; } + /** + * Get a list of conditions + * + * @return conditions + */ public Condition[] getConditions() { return conditions; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java index 501d2c078..7dcc29e32 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java @@ -1,6 +1,7 @@ package net.momirealms.customcrops.api.mechanic.condition; import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; +import org.jetbrains.annotations.Nullable; public class DeathConditions extends Conditions { @@ -15,14 +16,30 @@ public DeathConditions(Condition[] conditions, String deathItem, ItemCarrier ite this.deathDelay = deathDelay; } + /** + * Get the item to replace, null if the crop would be removed + * + * @return the item to replace + */ + @Nullable public String getDeathItem() { return deathItem; } + /** + * Get the item carrier of the item to replace + * + * @return item carrier + */ public ItemCarrier getItemCarrier() { return itemCarrier; } + /** + * Get the delay in ticks + * + * @return delay + */ public int getDeathDelay() { return deathDelay; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java index 6839679d3..7883c7fbe 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java @@ -52,14 +52,29 @@ public BoneMeal( this.dispenserAllowed = dispenserAllowed; } + /** + * Get the ID of the bone meal item + * + * @return bonemeal item id + */ public String getItem() { return item; } + /** + * Get the returned item's ID + * + * @return returned item ID + */ public String getReturned() { return returned; } + /** + * Get the points to gain in one try + * + * @return points + */ public int getPoint() { for (Pair pair : pointGainList) { if (Math.random() < pair.left()) { @@ -69,18 +84,38 @@ public int getPoint() { return 0; } + /** + * Trigger the actions of using the bone meal + * + * @param state player state + */ public void trigger(State state) { ActionManager.triggerActions(state, actions); } + /** + * Get the amount to consume + * + * @return amount to consume + */ public int getUsedAmount() { return usedAmount; } + /** + * Get the amount of the returned items + * + * @return amount of the returned items + */ public int getReturnedAmount() { return returnedAmount; } + /** + * If the bone meal can be used with a dispenser + * + * @return can be used or not + */ public boolean isDispenserAllowed() { return dispenserAllowed; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java index 29defa0fa..e378a98da 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java @@ -23,6 +23,7 @@ import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; import net.momirealms.customcrops.api.mechanic.requirement.Requirement; import net.momirealms.customcrops.api.mechanic.requirement.State; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -30,50 +31,169 @@ public interface Crop extends KeyItem { + /** + * Get the id of the seed + * + * @return seed ID + */ String getSeedItemID(); + /** + * Get the max points to grow + * + * @return max points + */ int getMaxPoints(); + /** + * Get the requirements for planting + * + * @return requirements for planting + */ Requirement[] getPlantRequirements(); + /** + * Get the requirements for breaking + * + * @return requirements for breaking + */ Requirement[] getBreakRequirements(); + /** + * Get the requirements for interactions + * + * @return requirements for interactions + */ Requirement[] getInteractRequirements(); + /** + * Get the conditions to grow + * + * @return conditions to grow + */ Conditions getGrowConditions(); + /** + * Get the conditions of death + * + * @return conditions of death + */ DeathConditions[] getDeathConditions(); + /** + * Get the available bone meals + * + * @return bone meals + */ BoneMeal[] getBoneMeals(); + /** + * If the crop has rotations + */ boolean hasRotation(); + /** + * Trigger actions + * + * @param trigger action trigger + * @param state player state + */ void trigger(ActionTrigger trigger, State state); + /** + * Get the stage config by point + * + * @param point point + * @return stage config + */ + @Nullable Stage getStageByPoint(int point); + /** + * Get the stage item ID by point + * This is always NotNull if the point is no lower than 0 + * + * @param point point + * @return the stage item ID + */ + @NotNull String getStageItemByPoint(int point); - Stage getStageByItemID(String itemID); - + /** + * Get stage config by stage item ID + * + * @param id item id + * @return stage config + */ + @Nullable + Stage getStageByItemID(String id); + + /** + * Get all the stages + * + * @return stages + */ Collection getStages(); + /** + * Get the pots to plant + * + * @return whitelisted pots + */ HashSet getPotWhitelist(); + /** + * Get the carrier of this crop + * + * @return carrier of this crop + */ ItemCarrier getItemCarrier(); interface Stage { + /** + * Get the offset of the hologram + * + * @return offset + */ double getHologramOffset(); - @Nullable String getStageID(); - + /** + * Get the stage item ID + * This can be null if this point doesn't have any state change + * + * @return stage item ID + */ + @Nullable + String getStageID(); + + /** + * Get the point of this stage + * + * @return point + */ int getPoint(); + /** + * Trigger actions + * + * @param trigger action trigger + * @param state player state + */ void trigger(ActionTrigger trigger, State state); + /** + * Get the requirements for interactions + * + * @return requirements for interactions + */ Requirement[] getInteractRequirements(); + /** + * Get the requirements for breaking + * + * @return requirements for breaking + */ Requirement[] getBreakRequirements(); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java index 3e44779db..17d50c776 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java @@ -23,19 +23,58 @@ import java.util.HashSet; public interface Fertilizer extends EventItem { + + /** + * Get the key + * + * @return key + */ String getKey(); + /** + * Get the item ID + * + * @return item ID + */ String getItemID(); + /** + * Get the max times of usage + * + * @return the max times of usage + */ int getTimes(); + /** + * Get the type of the fertilizer + * + * @return the type of the fertilizer + */ FertilizerType getFertilizerType(); + /** + * Get the pot whitelist + * + * @return pot whitelist + */ HashSet getPotWhitelist(); + /** + * If the fertilizer can only be used before planting + */ boolean isBeforePlant(); + /** + * Get the image of the fertilizer + * + * @return icon + */ String getIcon(); + /** + * Get the requirements for this fertilizer + * + * @return requirements + */ Requirement[] getRequirements(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java index df0513c45..973c5e0f3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java @@ -28,34 +28,110 @@ public interface Pot extends KeyItem { + /** + * Get max water storage + * + * @return water storage + */ int getStorage(); + /** + * Get the key + * + * @return key + */ String getKey(); + /** + * Get the blocks that belong to this pot + * + * @return blocks + */ HashSet getPotBlocks(); + /** + * Get the methods to fill this pot + * + * @return methods + */ PassiveFillMethod[] getPassiveFillMethods(); + /** + * Get the dry state + * + * @return dry state item ID + */ String getDryItem(); + /** + * Get the wet state + * + * @return wet state item ID + */ String getWetItem(); + /** + * Get the requirements for placement + * + * @return requirements for placement + */ Requirement[] getPlaceRequirements(); + /** + * Get the requirements for breaking + * + * @return requirements for breaking + */ Requirement[] getBreakRequirements(); + /** + * Get the requirements for using + * + * @return requirements for using + */ Requirement[] getUseRequirements(); + /** + * Trigger actions + * + * @param trigger action trigger + * @param state player state + */ void trigger(ActionTrigger trigger, State state); + /** + * Get the water bar images + * + * @return water bar images + */ WaterBar getWaterBar(); + /** + * Does the pot absorb raindrop + */ boolean isRainDropAccepted(); + + /** + * Does nearby water make the pot wet + */ boolean isNearbyWaterAccepted(); + /** + * Get the block ID by water and fertilizers + * + * @param water water + * @param type the type of the fertilizer + * @return block item ID + */ String getBlockState(boolean water, FertilizerType type); + /** + * Is the pot a vanilla blocks + */ boolean isVanillaBlock(); + /** + * Is the id a wet pot + */ boolean isWetPot(String id); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java index cc4d33f79..e9667aa40 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java @@ -21,5 +21,10 @@ public interface Scarecrow extends KeyItem { + /** + * Get the item ID + * + * @return item ID + */ String getItemID(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java index addf6d5ff..f34148a1c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java @@ -28,33 +28,107 @@ public interface Sprinkler extends KeyItem { + /** + * Get the 2D item ID + * + * @return 2D item ID + */ String get2DItemID(); + /** + * Get the 3D item ID + * + * @return 3D item ID + */ String get3DItemID(); + /** + * Get the 3D item ID (With water inside) + * + * @return 3D item ID (With water inside) + */ String get3DItemWithWater(); + /** + * Get the max storage of water + * + * @return max storage of water + */ int getStorage(); + /** + * Get the working range + * + * @return working range + */ int getRange(); + /** + * Is water infinite + */ boolean isInfinite(); + /** + * Get the amount of water to add to the pot during sprinkling + * + * @return amount of water to add to the pot during sprinkling + */ int getWater(); + /** + * Get the pots that receive the water + * + * @return whitelisted pots + */ HashSet getPotWhitelist(); + /** + * Get the carrier of the pot + * + * @return carrier of the pot + */ ItemCarrier getItemCarrier(); + /** + * Get methods to fill the sprinkler + * + * @return methods to fill the sprinkler + */ PassiveFillMethod[] getPassiveFillMethods(); + /** + * Get the requirements for placement + * + * @return requirements for placement + */ Requirement[] getPlaceRequirements(); + /** + * Get the requirements for breaking + * + * @return requirements for breaking + */ Requirement[] getBreakRequirements(); + /** + * Get the requirements for using + * + * @return requirements for using + */ Requirement[] getUseRequirements(); + /** + * Trigger actions + * + * @param trigger action trigger + * @param state player state + */ void trigger(ActionTrigger trigger, State state); + /** + * Get the water bar images + * + * @return water bar images + */ WaterBar getWaterBar(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java index 645eda5b6..fccad507d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java @@ -32,33 +32,107 @@ public interface WateringCan extends KeyItem { + /** + * Get the ID of the item + * + * @return item ID + */ String getItemID(); + /** + * Get the width of the effective range + * + * @return width + */ int getWidth(); + /** + * Get the length of the effective range + * + * @return length + */ int getLength(); + /** + * Get the storage of water + * + * @return storage of water + */ int getStorage(); + /** + * Get the amount of water to add in one try + */ int getWater(); + /** + * If the watering can has dynamic lore + */ boolean hasDynamicLore(); + /** + * Update a watering can's data + * + * @param player player + * @param itemStack watering can item + * @param water the amount of water + * @param args the placeholders + */ void updateItem(Player player, ItemStack itemStack, int water, Map args); + /** + * Get the current water + * + * @param itemStack watering can item + * @return current water + */ int getCurrentWater(ItemStack itemStack); + /** + * Get the pots that receive water from this watering can + * + * @return whitelisted pots + */ HashSet getPotWhitelist(); + /** + * Get the sprinklers that receive water from this watering can + * + * @return whitelisted sprinklers + */ HashSet getSprinklerWhitelist(); + /** + * Get the dynamic lores + * + * @return dynamic lores + */ List getLore(); + /** + * Get the water bar images + * + * @return water bar images + */ @Nullable WaterBar getWaterBar(); + /** + * Get the requirements for using this watering can + * + * @return requirements + */ Requirement[] getRequirements(); + /** + * If the water is infinite + */ boolean isInfinite(); + /** + * Trigger actions + * + * @param trigger action trigger + * @param state player state + */ void trigger(ActionTrigger trigger, State state); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java index d50b9b0f4..53004bf7f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java @@ -20,7 +20,18 @@ import net.momirealms.customcrops.api.mechanic.item.Fertilizer; public interface QualityCrop extends Fertilizer { + + /** + * Get the chance of taking effect + * + * @return chance + */ double getChance(); + /** + * Get the modified quality ratio + * + * @return the modified quality ratio + */ double[] getRatio(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java index aa01a20a8..0071623e1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java @@ -21,5 +21,10 @@ public interface SoilRetain extends Fertilizer { + /** + * Get the chance of taking effect + * + * @return chance + */ double getChance(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java index 23c0ec2f0..28a20b12d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java @@ -21,5 +21,10 @@ public interface SpeedGrow extends Fertilizer { + /** + * Get the extra points to gain + * + * @return points + */ int getPointBonus(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java index 727c62d86..b11e32b09 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java @@ -21,5 +21,10 @@ public interface Variation extends Fertilizer { + /** + * Get the bonus of variation chance + * + * @return chance bonus + */ double getChanceBonus(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java index cae5804d1..fbdfc5125 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java @@ -21,5 +21,10 @@ public interface YieldIncrease extends Fertilizer { + /** + * Get the extra amount to drop + * + * @return amount bonus + */ int getAmountBonus(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java index 54f4c442d..adbef453a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java @@ -36,18 +36,38 @@ public PassiveFillMethod(String used, int usedAmount, @Nullable String returned, this.returnedAmount = returnedAmount; } + /** + * Get the consumed item ID + * + * @return consumed item ID + */ public String getUsed() { return used; } + /** + * Get the returned item ID + * + * @return returned item ID + */ public String getReturned() { return returned; } + /** + * Get the amount to consume + * + * @return amount to consume + */ public int getUsedAmount() { return usedAmount; } + /** + * Get the amount of the returned items + * + * @return amount of the returned items + */ public int getReturnedAmount() { return returnedAmount; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java index 8d91b8f3b..df621f45a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java @@ -29,7 +29,12 @@ public PositiveFillMethod(String id, int amount, Action[] actions, Requirement[] this.id = id; } - public String getId() { + /** + * Get the block/furniture ID + * + * @return id + */ + public String getID() { return id; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/CRotation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/CRotation.java index 69f25e660..1e1fc4c5c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/CRotation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/CRotation.java @@ -1,5 +1,7 @@ package net.momirealms.customcrops.api.mechanic.misc; +import org.bukkit.Rotation; + public enum CRotation { NONE(0f), @@ -18,4 +20,39 @@ public enum CRotation { public float getYaw() { return yaw; } + + public static CRotation getByRotation(Rotation rotation) { + switch (rotation) { + default -> { + return CRotation.NONE; + } + case CLOCKWISE -> { + return CRotation.WEST; + } + case COUNTER_CLOCKWISE -> { + return CRotation.EAST; + } + case FLIPPED -> { + return CRotation.NORTH; + } + } + } + + public static CRotation getByYaw(float yaw) { + yaw = (Math.abs(yaw + 180) % 360); + switch ((int) (yaw/90)) { + case 1 -> { + return CRotation.WEST; + } + case 2 -> { + return CRotation.NORTH; + } + case 3 -> { + return CRotation.EAST; + } + default -> { + return CRotation.SOUTH; + } + } + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java index 503ba16c0..48e180205 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java @@ -21,5 +21,11 @@ public interface Value { + /** + * Get double value + * + * @param player player + * @return the value + */ double get(Player player); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java index e70847841..e92876c9b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java @@ -19,5 +19,11 @@ public interface Requirement { + /** + * Does player meet the requirement + * + * @param state state of player + * @return met or not + */ boolean isStateMet(State state); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java index 9f9972fac..0b70b6ae5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java @@ -19,11 +19,31 @@ public abstract class RequirementExpansion { + /** + * Get the version number + * + * @return version + */ public abstract String getVersion(); + /** + * Get the author + * + * @return author + */ public abstract String getAuthor(); + /** + * Get the type of the requirement + * + * @return the type of the requirement + */ public abstract String getRequirementType(); + /** + * Get the requirement factory + * + * @return the requirement factory + */ public abstract RequirementFactory getRequirementFactory(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java index cf429ec11..4fa5eec7b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java @@ -23,8 +23,22 @@ public interface RequirementFactory { + /** + * Build a requirement + * + * @param args args + * @param notMetActions actions to perform if the requirement is not met + * @param advanced whether to trigger the notMetActions or not + * @return requirement + */ Requirement build(Object args, List notMetActions, boolean advanced); + /** + * Build a requirement + * + * @param args args + * @return requirement + */ default Requirement build(Object args) { return build(args, null, false); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java index 2f2fa6f94..1aae7f8f3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java @@ -22,5 +22,10 @@ public interface CustomCropsBlock extends DataBlock, Tickable { + /** + * Get the type of the item + * + * @return type of the item + */ ItemType getType(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java index db9576650..031d04593 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java @@ -2,5 +2,11 @@ public interface Tickable { + /** + * Tick + * + * @param interval interval + * @param offline offline tick + */ void tick(int interval, boolean offline); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java index 23ad4757e..56b0b7a41 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java @@ -33,26 +33,55 @@ public AbstractCustomCropsBlock(SimpleLocation location, CompoundMap compoundMap this.location = location; } + /** + * Set data by key + * + * @param key key + * @param tag data tag + */ @Override public void setData(String key, Tag tag) { compoundMap.put(key, tag); } + /** + * Get data tag by key + * + * @param key key + * @return data tag + */ @Override - public Tag getData(String name) { - return compoundMap.get(name); + public Tag getData(String key) { + return compoundMap.get(key); } + /** + * Get the data map + * + * @return data map + */ @Override public SynchronizedCompoundMap getCompoundMap() { return compoundMap; } + /** + * Get the location of the block + * + * @return location + */ @Override public SimpleLocation getLocation() { return location; } + /** + * interval is customized + * You can perform tick logics if the block is ticked for some times + * + * @param interval interval + * @return can be ticked or not + */ public boolean canTick(int interval) { if (interval == 1) { return true; diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java index 1ac381209..93b7457ef 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java @@ -24,84 +24,310 @@ import net.momirealms.customcrops.api.mechanic.world.ChunkPos; import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import org.jetbrains.annotations.Nullable; import java.util.Optional; public interface CustomCropsChunk { + /** + * Calculate the unload time and perform compensation + * This method can only be called in a loaded chunk + */ void notifyOfflineUpdates(); + /** + * Get the world associated with the chunk + * + * @return CustomCrops world + */ CustomCropsWorld getCustomCropsWorld(); + /** + * Get the region associated with the chunk + * + * @return CustomCrops region + */ CustomCropsRegion getCustomCropsRegion(); + /** + * Get the position of the chunk + * + * @return chunk position + */ ChunkPos getChunkPos(); + /** + * Do second timer + */ void secondTimer(); + /** + * Get the unloaded time in seconds + * This value would increase if the chunk is lazy + * + * @return the unloaded time + */ + int getUnloadedSeconds(); + + /** + * Set the unloaded seconds + * + * @param unloadedSeconds unloadedSeconds + */ + void setUnloadedSeconds(int unloadedSeconds); + + /** + * Get the last loaded time + * + * @return last loaded time + */ long getLastLoadedTime(); + /** + * Set the last loaded time to latest + */ + void updateLastLoadedTime(); + + /** + * Get the loaded time in seconds + * + * @return loaded time + */ int getLoadedSeconds(); + /** + * Get crop at a certain location + * + * @param location location + * @return crop data + */ Optional getCropAt(SimpleLocation location); + /** + * Get sprinkler at a certain location + * + * @param location location + * @return sprinkler data + */ Optional getSprinklerAt(SimpleLocation location); + /** + * Get pot at a certain location + * + * @param location location + * @return pot data + */ Optional getPotAt(SimpleLocation location); + /** + * Get greenhouse glass at a certain location + * + * @param location location + * @return greenhouse glass data + */ Optional getGlassAt(SimpleLocation location); + /** + * Get scarecrow at a certain location + * + * @param location location + * @return scarecrow data + */ Optional getScarecrowAt(SimpleLocation location); + /** + * Get block data at a certain location + * + * @param location location + * @return block data + */ Optional getBlockAt(SimpleLocation location); - void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount); - + /** + * Add water to the sprinkler + * This method would create new sprinkler data if the sprinkler data not exists in that place + * This method would also update the sprinkler's model if it has models according to the water amount + * + * @param sprinkler sprinkler config + * @param amount amount of water + * @param location location + */ + void addWaterToSprinkler(Sprinkler sprinkler, int amount, SimpleLocation location); + + /** + * Add fertilizer to the pot + * This method would create new pot data if the pot data not exists in that place + * This method would update the pot's block state if it has appearance variations for different fertilizers + * + * @param pot pot config + * @param fertilizer fertilizer config + * @param location location + */ void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location); - void addWaterToPot(Pot pot, SimpleLocation location, int amount); - - void removeSprinklerAt(SimpleLocation location); - - void removePotAt(SimpleLocation location); - - void removeCropAt(SimpleLocation location); - - void removeGlassAt(SimpleLocation location); - - void removeScarecrowAt(SimpleLocation location); - + /** + * Add water to the pot + * This method would create new pot data if the pot data not exists in that place + * This method would update the pot's block state if it's dry + * + * @param pot pot config + * @param amount amount of water + * @param location location + */ + void addWaterToPot(Pot pot, int amount, SimpleLocation location); + + /** + * Add points to a crop + * This method would do nothing if the crop data not exists in that place + * This method would change the stage of the crop and trigger the actions + * + * @param crop crop config + * @param points points to add + * @param location location + */ + void addPointToCrop(Crop crop, int points, SimpleLocation location); + + /** + * Remove sprinkler data from a certain location + * + * @param location location + */ + @Nullable + WorldSprinkler removeSprinklerAt(SimpleLocation location); + + /** + * Remove pot data from a certain location + * + * @param location location + */ + @Nullable + WorldPot removePotAt(SimpleLocation location); + + /** + * Remove crop data from a certain location + * + * @param location location + */ + @Nullable + WorldCrop removeCropAt(SimpleLocation location); + + /** + * Remove greenhouse glass data from a certain location + * + * @param location location + */ + @Nullable + WorldGlass removeGlassAt(SimpleLocation location); + + /** + * Remove scarecrow data from a certain location + * + * @param location location + */ + @Nullable + WorldScarecrow removeScarecrowAt(SimpleLocation location); + + /** + * Remove any block data from a certain location + * + * @param location location + * @return block data + */ + @Nullable CustomCropsBlock removeBlockAt(SimpleLocation location); + /** + * Add a custom block data at a certain location + * + * @param block block data + * @param location location + * @return the previous block data + */ + @Nullable CustomCropsBlock addBlockAt(CustomCropsBlock block, SimpleLocation location); + /** + * Get the amount of crops in this chunk + * + * @return the amount of crops + */ int getCropAmount(); + /** + * Get the amount of pots in this chunk + * + * @return the amount of pots + */ int getPotAmount(); + /** + * Get the amount of sprinklers in this chunk + * + * @return the amount of sprinklers + */ int getSprinklerAmount(); + /** + * Add pot data + * + * @param pot pot data + * @param location location + */ void addPotAt(WorldPot pot, SimpleLocation location); + /** + * Add sprinkler data + * + * @param sprinkler sprinkler data + * @param location location + */ void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location); + /** + * Add crop data + * + * @param crop crop data + * @param location location + */ void addCropAt(WorldCrop crop, SimpleLocation location); - void addPointToCrop(Crop crop, SimpleLocation location, int points); - + /** + * Add greenhouse glass data + * + * @param glass glass data + * @param location location + */ void addGlassAt(WorldGlass glass, SimpleLocation location); + /** + * Add scarecrow data + * + * @param scarecrow scarecrow data + * @param location location + */ void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location); - void updateLastLoadedTime(); - + /** + * Get CustomCrops sections + * + * @return sections + */ CustomCropsSection[] getSections(); + /** + * Get section by ID + * + * @param sectionID id + * @return section + */ + @Nullable CustomCropsSection getSection(int sectionID); - int getUnloadedSeconds(); - - void setUnloadedSeconds(int unloadedSeconds); - + /** + * If the chunk can be pruned + * + * @return can be pruned or not + */ boolean canPrune(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsRegion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsRegion.java index 28254197c..9852347e2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsRegion.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsRegion.java @@ -25,17 +25,52 @@ public interface CustomCropsRegion { + /** + * Get the CustomCrops world associated with the region + * + * @return CustomCrops world + */ CustomCropsWorld getCustomCropsWorld(); + /** + * Get the cached chunk + * + * @param pos chunk position + * @return cached chunk in bytes + */ byte @Nullable [] getChunkBytes(ChunkPos pos); + /** + * Get the position of the region + * + * @return the position of the region + */ RegionPos getRegionPos(); + /** + * Remove a chunk by the position + * + * @param pos the position of the chunk + */ void removeChunk(ChunkPos pos); + /** + * Put a chunk's data to cache + * + * @param pos the position of the chunk + * @param data the serialized data + */ void saveChunk(ChunkPos pos, byte[] data); + /** + * Get the data to save + * + * @return the data to save + */ Map getRegionDataToSave(); + /** + * If the region can be pruned or not + */ boolean canPrune(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java index 848440a73..d2fff018f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java @@ -19,22 +19,63 @@ import net.momirealms.customcrops.api.mechanic.world.BlockPos; import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import org.jetbrains.annotations.Nullable; import java.util.Map; public interface CustomCropsSection { + /** + * Get the section ID + * + * @return section ID + */ int getSectionID(); + /** + * Get block at a certain position + * + * @param pos block position + * @return the block + */ + @Nullable CustomCropsBlock getBlockAt(BlockPos pos); + /** + * Remove a block by a certain position + * + * @param pos block position + * @return the removed block + */ + @Nullable CustomCropsBlock removeBlockAt(BlockPos pos); + /** + * Add block at a certain position + * + * @param pos block position + * @param block the new block + * @return the previous block + */ + @Nullable CustomCropsBlock addBlockAt(BlockPos pos, CustomCropsBlock block); + /** + * If the section can be pruned or not + */ boolean canPrune(); + /** + * Get the blocks in this section + * + * @return blocks + */ CustomCropsBlock[] getBlocks(); + /** + * Get the block map + * + * @return block map + */ Map getBlockMap(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java index 523908fb6..b22bce52b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java @@ -27,6 +27,7 @@ import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.season.Season; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -34,96 +35,376 @@ public interface CustomCropsWorld { + /** + * Save all the data + */ void save(); + /** + * Start the tick system + */ void startTick(); + /** + * Stop the tick system + */ void cancelTick(); + /** + * Check if a region is loaded + * + * @param regionPos region position + * @return loaded or not + */ boolean isRegionLoaded(RegionPos regionPos); + /** + * Removed lazy chunks (Delayed unloading chunks) + * + * @param chunkPos chunk position + * @return the removed lazy chunk + */ CustomCropsChunk removeLazyChunkAt(ChunkPos chunkPos); + /** + * Get the world's settings + * + * @return world settings + */ WorldSetting getWorldSetting(); + /** + * Set the world's settings + * + * @param setting settings + */ void setWorldSetting(WorldSetting setting); + /** + * Get all the chunks + * + * @return chunks + */ Collection getChunkStorage(); + /** + * Get bukkit world + * This would be null if the world is unloaded + * + * @return bukkit world + */ + @Nullable World getWorld(); + /** + * Get the world's name + * + * @return world's name + */ + @NotNull String getWorldName(); + /** + * Check if the chunk is loaded + * The chunk would only be loaded when it has CustomCrops data and being loaded by minecraft chunk system + * + * @param chunkPos chunk position + * @return loaded or not + */ boolean isChunkLoaded(ChunkPos chunkPos); + /** + * Create CustomCrops chunk at a certain chunk position + * This method can only be called if that chunk is loaded + * Otherwise it would fail + * + * @param chunkPos chunk position + * @return the generated CustomCrops chunk + */ Optional getOrCreateLoadedChunkAt(ChunkPos chunkPos); + /** + * Get the loaded CustomCrops chunk at a certain chunk position + * + * @param chunkPos chunk position + * @return CustomCrops chunk + */ Optional getLoadedChunkAt(ChunkPos chunkPos); + /** + * Get the loaded CustomCrops region at a certain region position + * + * @param regionPos region position + * @return CustomCrops region + */ Optional getLoadedRegionAt(RegionPos regionPos); + /** + * Load a CustomCrops region + * You don't need to worry about the unloading since CustomCrops would unload the region + * if it's unused + * + * @param region region + */ void loadRegion(CustomCropsRegion region); + /** + * Load a CustomCrops chunk + * It's unsafe to call this method. Please use world.getChunkAt instead + * + * @param chunk chunk + */ void loadChunk(CustomCropsChunk chunk); + /** + * Unload a CustomCrops chunk + * It's unsafe to call this method + * + * @param chunkPos chunk position + */ void unloadChunk(ChunkPos chunkPos); + /** + * Delete a chunk's data + * + * @param chunkPos chunk position + */ void deleteChunk(ChunkPos chunkPos); + /** + * Set the season and date of the world + * + * @param infoData info data + */ void setInfoData(WorldInfoData infoData); + /** + * Get the season and date + * This might return the wrong value if sync-seasons is enabled + * + * @return info data + */ WorldInfoData getInfoData(); + /** + * Get the date of the world + * This would return the reference world's date if sync-seasons is enabled + * + * @return date + */ int getDate(); + /** + * Get the season of the world + * This would return the reference world's season if sync-seasons is enabled + * + * @return date + */ @Nullable Season getSeason(); + /** + * Get sprinkler at a certain location + * + * @param location location + * @return sprinkler data + */ Optional getSprinklerAt(SimpleLocation location); + /** + * Get pot at a certain location + * + * @param location location + * @return pot data + */ Optional getPotAt(SimpleLocation location); + /** + * Get crop at a certain location + * + * @param location location + * @return crop data + */ Optional getCropAt(SimpleLocation location); + /** + * Get greenhouse glass at a certain location + * + * @param location location + * @return greenhouse glass data + */ Optional getGlassAt(SimpleLocation location); + /** + * Get scarecrow at a certain location + * + * @param location location + * @return scarecrow data + */ Optional getScarecrowAt(SimpleLocation location); + /** + * Get block data at a certain location + * + * @param location location + * @return block data + */ Optional getBlockAt(SimpleLocation location); - void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount); - + /** + * Add water to the sprinkler + * This method would create new sprinkler data if the sprinkler data not exists in that place + * This method would also update the sprinkler's model if it has models according to the water amount + * + * @param sprinkler sprinkler config + * @param amount amount of water + * @param location location + */ + void addWaterToSprinkler(Sprinkler sprinkler, int amount, SimpleLocation location); + + /** + * Add fertilizer to the pot + * This method would create new pot data if the pot data not exists in that place + * This method would update the pot's block state if it has appearance variations for different fertilizers + * + * @param pot pot config + * @param fertilizer fertilizer config + * @param location location + */ void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location); - void addWaterToPot(Pot pot, SimpleLocation location, int amount); - - void removeSprinklerAt(SimpleLocation location); - - void removePotAt(SimpleLocation location); + /** + * Add water to the pot + * This method would create new pot data if the pot data not exists in that place + * This method would update the pot's block state if it's dry + * + * @param pot pot config + * @param amount amount of water + * @param location location + */ + void addWaterToPot(Pot pot, int amount, SimpleLocation location); + + /** + * Add points to a crop + * This method would do nothing if the crop data not exists in that place + * This method would change the stage of the crop and trigger the actions + * + * @param crop crop config + * @param points points to add + * @param location location + */ + void addPointToCrop(Crop crop, int points, SimpleLocation location); + + /** + * Remove sprinkler data from a certain location + * + * @param location location + */ + @Nullable + WorldSprinkler removeSprinklerAt(SimpleLocation location); - void removeCropAt(SimpleLocation location); + /** + * Remove pot data from a certain location + * + * @param location location + */ + @Nullable + WorldPot removePotAt(SimpleLocation location); - void removeGlassAt(SimpleLocation location); + /** + * Remove crop data from a certain location + * + * @param location location + */ + @Nullable + WorldCrop removeCropAt(SimpleLocation location); - void removeScarecrowAt(SimpleLocation location); + /** + * Remove greenhouse glass data from a certain location + * + * @param location location + */ + @Nullable + WorldGlass removeGlassAt(SimpleLocation location); + /** + * Remove scarecrow data from a certain location + * + * @param location location + */ + @Nullable + WorldScarecrow removeScarecrowAt(SimpleLocation location); + + /** + * Remove any block data from a certain location + * + * @param location location + * @return block data + */ + @Nullable CustomCropsBlock removeAnythingAt(SimpleLocation location); + /** + * If the amount of pot reaches the limitation + * + * @param location location + * @return reach or not + */ boolean isPotReachLimit(SimpleLocation location); + /** + * If the amount of crop reaches the limitation + * + * @param location location + * @return reach or not + */ boolean isCropReachLimit(SimpleLocation location); + /** + * If the amount of sprinkler reaches the limitation + * + * @param location location + * @return reach or not + */ boolean isSprinklerReachLimit(SimpleLocation location); + /** + * Add pot data + * + * @param pot pot data + * @param location location + */ void addPotAt(WorldPot pot, SimpleLocation location); + /** + * Add sprinkler data + * + * @param sprinkler sprinkler data + * @param location location + */ void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location); + /** + * Add crop data + * + * @param crop crop data + * @param location location + */ void addCropAt(WorldCrop crop, SimpleLocation location); - void addPointToCrop(Crop crop, SimpleLocation location, int points); - + /** + * Add greenhouse glass data + * + * @param glass glass data + * @param location location + */ void addGlassAt(WorldGlass glass, SimpleLocation location); + /** + * Add scarecrow data + * + * @param scarecrow scarecrow data + * @param location location + */ void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java index 6f1367598..0236184a7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java @@ -23,11 +23,33 @@ public interface DataBlock { + /** + * Set data by key + * + * @param key key + * @param tag data tag + */ void setData(String key, Tag tag); + /** + * Get data tag by key + * + * @param key key + * @return data tag + */ Tag getData(String key); + /** + * Get the data map + * + * @return data map + */ SynchronizedCompoundMap getCompoundMap(); + /** + * Get the location of the block + * + * @return location + */ SimpleLocation getLocation(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java index 2667aa7ab..e56daeee9 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java @@ -22,11 +22,31 @@ public interface WorldCrop extends CustomCropsBlock { + /** + * Get the key of the crop + * + * @return key + */ String getKey(); + /** + * Get the point + * + * @return point + */ int getPoint(); + /** + * Set the point + * + * @param point point + */ void setPoint(int point); + /** + * Get the config + * + * @return crop config + */ Crop getConfig(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java index 42cd79361..15a828c18 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.mechanic.world.level; import com.google.gson.annotations.SerializedName; +import net.momirealms.customcrops.api.manager.ConfigManager; import net.momirealms.customcrops.api.mechanic.world.season.Season; public class WorldInfoData { @@ -36,19 +37,39 @@ public static WorldInfoData empty() { return new WorldInfoData(Season.SPRING, 1); } + /** + * Get season + * + * @return season + */ public Season getSeason() { if (season == null) season = Season.SPRING; return season; } + /** + * Set season + * + * @param season the new season + */ public void setSeason(Season season) { this.season = season; } + /** + * Get date + * + * @return date + */ public int getDate() { return date; } + /** + * Set date + * + * @param date the new date + */ public void setDate(int date) { this.date = date; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java index 2d35b6eb1..7cd7a800e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java @@ -20,26 +20,75 @@ import net.momirealms.customcrops.api.mechanic.item.Fertilizer; import net.momirealms.customcrops.api.mechanic.item.Pot; import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public interface WorldPot extends CustomCropsBlock { + /** + * Get the key of the pot + * + * @return key + */ String getKey(); + /** + * Get the amount of water + * + * @return amount of water + */ int getWater(); + /** + * Set the amount of water + * + * @param water water + */ void setWater(int water); + /** + * Get the fertilizer config + * + * @return fertilizer config + */ + @Nullable Fertilizer getFertilizer(); - void setFertilizer(Fertilizer fertilizer); + /** + * Set the fertilizer + * + * @param fertilizer fertilizer + */ + void setFertilizer(@NotNull Fertilizer fertilizer); + /** + * Remove the fertilizer + */ void removeFertilizer(); + /** + * Get the remaining usages of the fertilizer + * + * @return remaining usages of the fertilizer + */ int getFertilizerTimes(); + /** + * Set the remaining usages of the fertilizer + * + * @param times the remaining usages of the fertilizer + */ void setFertilizerTimes(int times); + /** + * Get the pot config + * + * @return pot config + */ Pot getConfig(); - void tickWater(CustomCropsChunk chunk); + /** + * Tick the rainwater and nearby water + */ + void tickWater(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java index b26f2ec72..9cab54d01 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java @@ -22,11 +22,31 @@ public interface WorldSprinkler extends CustomCropsBlock { + /** + * Get the amount of water + * + * @return amount of water + */ int getWater(); + /** + * Set the amount of water + * + * @param water amount of water + */ void setWater(int water); + /** + * Get the key of the sprinkler + * + * @return key + */ String getKey(); + /** + * Get the sprinkler config + * + * @return sprinkler config + */ Sprinkler getConfig(); } diff --git a/build.gradle.kts b/build.gradle.kts index 5e62fbfb4..7909f6af9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.4.4" + project.version = "3.4.5" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 28e6696ea..01d08dd69 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -51,7 +51,7 @@ dependencies { implementation(project(":legacy-api")) implementation("net.kyori:adventure-api:4.15.0") implementation("net.kyori:adventure-platform-bukkit:4.3.2") - implementation("com.github.Xiao-MoMi:AntiGriefLib:0.10") + implementation("com.github.Xiao-MoMi:AntiGriefLib:0.11") implementation("com.github.Xiao-MoMi:BiomeAPI:0.3") compileOnly("net.kyori:adventure-text-minimessage:4.15.0") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java index 2b9a3417c..ce1f80a18 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java @@ -81,7 +81,7 @@ default CRotation removeAnythingAt(Location location) { CRotation previousCRotation; Entity first = entities.stream().findFirst().get(); if (first instanceof ItemFrame itemFrame) { - previousCRotation = RotationUtils.getCRotation(itemFrame.getRotation()); + previousCRotation = CRotation.getByRotation(itemFrame.getRotation()); } else if (VersionManager.isHigherThan1_19_R3()) { previousCRotation = DisplayEntityUtils.getRotation(first); } else { @@ -109,4 +109,27 @@ default String getSomethingAt(Location location) { } return "AIR"; } + + default CRotation getRotation(Location location) { + if (location.getBlock().getType() == Material.AIR) { + Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toCenterLocation(location), 0.5,0.51,0.5); + entities.removeIf(entity -> { + EntityType type = entity.getType(); + return type != EntityType.ITEM_FRAME + && (!VersionManager.isHigherThan1_19_R3() || type != EntityType.ITEM_DISPLAY); + }); + if (entities.size() == 0) return CRotation.NONE; + CRotation rotation; + Entity first = entities.stream().findFirst().get(); + if (first instanceof ItemFrame itemFrame) { + rotation = CRotation.getByRotation(itemFrame.getRotation()); + } else if (VersionManager.isHigherThan1_19_R3()) { + rotation = DisplayEntityUtils.getRotation(first); + } else { + rotation = CRotation.NONE; + } + return rotation; + } + return CRotation.NONE; + } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index f17ab16a1..ef108f14a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -372,6 +372,11 @@ public CRotation removeAnythingAt(Location location) { return customProvider.removeAnythingAt(location); } + @Override + public CRotation getRotation(Location location) { + return customProvider.getRotation(location); + } + @Override public WateringCan getWateringCanByID(@NotNull String id) { return id2WateringCanMap.get(id); @@ -1117,7 +1122,7 @@ private void loadWateringCan(String key, ConfigurationSection section) { if (!wateringCan.isInfinite()) { PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); for (PositiveFillMethod method : methods) { - if (method.getId().equals(clickedFurnitureID)) { + if (method.getID().equals(clickedFurnitureID)) { if (method.canFill(state)) { // fire the event WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, location, wateringCan, method); @@ -1176,7 +1181,7 @@ private void loadWateringCan(String key, ConfigurationSection section) { int water = wateringCan.getCurrentWater(itemInHand); PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); for (PositiveFillMethod method : methods) { - if (method.getId().equals(blockID)) { + if (method.getID().equals(blockID)) { if (method.canFill(state)) { if (water < wateringCan.getStorage()) { // fire the event @@ -1230,7 +1235,7 @@ private void loadWateringCan(String key, ConfigurationSection section) { int water = wateringCan.getCurrentWater(itemInHand); PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); for (PositiveFillMethod method : methods) { - if (method.getId().equals(blockID)) { + if (method.getID().equals(blockID)) { if (method.canFill(state)) { if (water < wateringCan.getStorage()) { // fire the event diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java index 5251d4fac..2b746fe63 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java @@ -26,6 +26,7 @@ import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; import net.momirealms.customcrops.api.mechanic.requirement.Requirement; import net.momirealms.customcrops.mechanic.item.AbstractEventItem; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -143,6 +144,7 @@ public Stage getStageByPoint(int point) { return point2StageConfigMap.get(point); } + @NotNull @Override public String getStageItemByPoint(int point) { if (point >= 0) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java index d8470efa5..b74f1c3d1 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -206,7 +206,7 @@ private void randomTick(WorldSetting setting, boolean offline) { } } case POT -> { - ((WorldPot) block).tickWater(this); + ((WorldPot) block).tickWater(); if (setting.randomTickPot()) { block.tick(setting.getTickPotInterval(), offline); } @@ -277,7 +277,7 @@ public Optional getScarecrowAt(SimpleLocation simpleLocation) { } @Override - public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount) { + public void addWaterToSprinkler(Sprinkler sprinkler, int amount, SimpleLocation location) { Optional optionalSprinkler = getSprinklerAt(location); if (optionalSprinkler.isEmpty()) { addBlockAt(new MemorySprinkler(location, sprinkler.getKey(), amount), location); @@ -314,7 +314,7 @@ public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation lo } @Override - public void addWaterToPot(Pot pot, SimpleLocation location, int amount) { + public void addWaterToPot(Pot pot, int amount, SimpleLocation location) { Optional optionalWorldPot = getPotAt(location); if (optionalWorldPot.isEmpty()) { MemoryPot memoryPot = new MemoryPot(location, pot.getKey()); @@ -365,7 +365,7 @@ public void addCropAt(WorldCrop crop, SimpleLocation location) { } @Override - public void addPointToCrop(Crop crop, SimpleLocation location, int points) { + public void addPointToCrop(Crop crop, int points, SimpleLocation location) { if (points <= 0) return; Optional cropData = getCropAt(location); if (cropData.isEmpty()) { @@ -415,52 +415,72 @@ public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { } @Override - public void removeSprinklerAt(SimpleLocation location) { + public WorldSprinkler removeSprinklerAt(SimpleLocation location) { CustomCropsBlock removed = removeBlockAt(location); if (removed == null) { CustomCropsPlugin.get().debug("Failed to remove sprinkler from " + location + " because sprinkler doesn't exist."); - } else if (!(removed instanceof WorldSprinkler)) { + return null; + } else if (!(removed instanceof WorldSprinkler worldSprinkler)) { CustomCropsPlugin.get().debug("Removed sprinkler from " + location + " but the previous block type is " + removed.getType().name()); + return null; + } else { + return worldSprinkler; } } @Override - public void removePotAt(SimpleLocation location) { + public WorldPot removePotAt(SimpleLocation location) { CustomCropsBlock removed = removeBlockAt(location); if (removed == null) { CustomCropsPlugin.get().debug("Failed to remove pot from " + location + " because pot doesn't exist."); - } else if (!(removed instanceof WorldPot)) { + return null; + } else if (!(removed instanceof WorldPot worldPot)) { CustomCropsPlugin.get().debug("Removed pot from " + location + " but the previous block type is " + removed.getType().name()); + return null; + } else { + return worldPot; } } @Override - public void removeCropAt(SimpleLocation location) { + public WorldCrop removeCropAt(SimpleLocation location) { CustomCropsBlock removed = removeBlockAt(location); if (removed == null) { CustomCropsPlugin.get().debug("Failed to remove crop from " + location + " because crop doesn't exist."); - } else if (!(removed instanceof WorldCrop)) { + return null; + } else if (!(removed instanceof WorldCrop worldCrop)) { CustomCropsPlugin.get().debug("Removed crop from " + location + " but the previous block type is " + removed.getType().name()); + return null; + } else { + return worldCrop; } } @Override - public void removeGlassAt(SimpleLocation location) { + public WorldGlass removeGlassAt(SimpleLocation location) { CustomCropsBlock removed = removeBlockAt(location); if (removed == null) { CustomCropsPlugin.get().debug("Failed to remove glass from " + location + " because glass doesn't exist."); - } else if (!(removed instanceof WorldGlass)) { + return null; + } else if (!(removed instanceof WorldGlass worldGlass)) { CustomCropsPlugin.get().debug("Removed glass from " + location + " but the previous block type is " + removed.getType().name()); + return null; + } else { + return worldGlass; } } @Override - public void removeScarecrowAt(SimpleLocation location) { + public WorldScarecrow removeScarecrowAt(SimpleLocation location) { CustomCropsBlock removed = removeBlockAt(location); if (removed == null) { CustomCropsPlugin.get().debug("Failed to remove scarecrow from " + location + " because scarecrow doesn't exist."); - } else if (!(removed instanceof WorldScarecrow)) { + return null; + } else if (!(removed instanceof WorldScarecrow worldScarecrow)) { CustomCropsPlugin.get().debug("Removed scarecrow from " + location + " but the previous block type is " + removed.getType().name()); + return null; + } else { + return worldScarecrow; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index a944ca91e..b6898bfb0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -39,6 +39,7 @@ import net.momirealms.customcrops.util.EventUtils; import org.bukkit.Bukkit; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.ref.WeakReference; @@ -217,6 +218,7 @@ public World getWorld() { }); } + @NotNull @Override public String getWorldName() { return worldName; @@ -359,10 +361,10 @@ public Optional getBlockAt(SimpleLocation location) { } @Override - public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount) { + public void addWaterToSprinkler(Sprinkler sprinkler, int amount, SimpleLocation location) { Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { - chunk.get().addWaterToSprinkler(sprinkler, location, amount); + chunk.get().addWaterToSprinkler(sprinkler, amount, location); } else { LogUtils.warn("Invalid operation: Adding water to sprinkler in a not generated chunk"); } @@ -379,10 +381,10 @@ public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation lo } @Override - public void addWaterToPot(Pot pot, SimpleLocation location, int amount) { + public void addWaterToPot(Pot pot, int amount, SimpleLocation location) { Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { - chunk.get().addWaterToPot(pot, location, amount); + chunk.get().addWaterToPot(pot, amount, location); } else { LogUtils.warn("Invalid operation: Adding water to pot in a not generated chunk"); } @@ -419,10 +421,10 @@ public void addCropAt(WorldCrop crop, SimpleLocation location) { } @Override - public void addPointToCrop(Crop crop, SimpleLocation location, int points) { + public void addPointToCrop(Crop crop, int points, SimpleLocation location) { Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { - chunk.get().addPointToCrop(crop, location, points); + chunk.get().addPointToCrop(crop, points, location); } else { LogUtils.warn("Invalid operation: Adding point to crop in a not generated chunk"); } @@ -449,52 +451,57 @@ public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { } @Override - public void removeSprinklerAt(SimpleLocation location) { + public WorldSprinkler removeSprinklerAt(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { - chunk.get().removeSprinklerAt(location); + return chunk.get().removeSprinklerAt(location); } else { - LogUtils.warn("Invalid operation: Removing sprinkler from an unloaded chunk"); + LogUtils.warn("Invalid operation: Removing sprinkler from an unloaded/empty chunk"); + return null; } } @Override - public void removePotAt(SimpleLocation location) { + public WorldPot removePotAt(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { - chunk.get().removePotAt(location); + return chunk.get().removePotAt(location); } else { - LogUtils.warn("Invalid operation: Removing pot from an unloaded chunk"); + LogUtils.warn("Invalid operation: Removing pot from an unloaded/empty chunk"); + return null; } } @Override - public void removeCropAt(SimpleLocation location) { + public WorldCrop removeCropAt(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { - chunk.get().removeCropAt(location); + return chunk.get().removeCropAt(location); } else { - LogUtils.warn("Invalid operation: Removing crop from an unloaded chunk"); + LogUtils.warn("Invalid operation: Removing crop from an unloaded/empty chunk"); + return null; } } @Override - public void removeGlassAt(SimpleLocation location) { + public WorldGlass removeGlassAt(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { - chunk.get().removeGlassAt(location); + return chunk.get().removeGlassAt(location); } else { - LogUtils.warn("Invalid operation: Removing glass from an unloaded chunk"); + LogUtils.warn("Invalid operation: Removing glass from an unloaded/empty chunk"); + return null; } } @Override - public void removeScarecrowAt(SimpleLocation location) { + public WorldScarecrow removeScarecrowAt(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); if (chunk.isPresent()) { - chunk.get().removeScarecrowAt(location); + return chunk.get().removeScarecrowAt(location); } else { - LogUtils.warn("Invalid operation: Removing scarecrow from an unloaded chunk"); + LogUtils.warn("Invalid operation: Removing scarecrow from an unloaded/empty chunk"); + return null; } } @@ -504,7 +511,7 @@ public CustomCropsBlock removeAnythingAt(SimpleLocation location) { if (chunk.isPresent()) { return chunk.get().removeBlockAt(location); } else { - LogUtils.warn("Invalid operation: Removing anything from an unloaded chunk"); + LogUtils.warn("Invalid operation: Removing anything from an unloaded/empty chunk"); return null; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index 32952db1d..9de5ae474 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -321,7 +321,7 @@ public void addWaterToSprinkler(@NotNull Sprinkler sprinkler, @NotNull SimpleLoc LogUtils.warn("Unsupported operation: Adding water to sprinkler in unloaded world " + location); return; } - cWorld.addWaterToSprinkler(sprinkler, location, amount); + cWorld.addWaterToSprinkler(sprinkler, amount, location); } @Override @@ -342,7 +342,7 @@ public void addWaterToPot(@NotNull Pot pot, int amount, @NotNull SimpleLocation LogUtils.warn("Unsupported operation: Adding water to pot in unloaded world " + location); return; } - cWorld.addWaterToPot(pot, location, amount); + cWorld.addWaterToPot(pot, amount, location); } @Override @@ -382,7 +382,7 @@ public void addPointToCrop(@NotNull Crop crop, int points, @NotNull SimpleLocati LogUtils.warn("Unsupported operation: Adding point to crop in unloaded world " + location); return; } - cWorld.addPointToCrop(crop, location, points); + cWorld.addPointToCrop(crop, points, location); } @Override @@ -406,53 +406,53 @@ public void addScarecrowAt(@NotNull WorldScarecrow scarecrow, @NotNull SimpleLoc } @Override - public void removeSprinklerAt(@NotNull SimpleLocation location) { + public WorldSprinkler removeSprinklerAt(@NotNull SimpleLocation location) { CWorld cWorld = loadedWorlds.get(location.getWorldName()); if (cWorld == null) { LogUtils.warn("Unsupported operation: Removing sprinkler from unloaded world " + location); - return; + return null; } - cWorld.removeSprinklerAt(location); + return cWorld.removeSprinklerAt(location); } @Override - public void removePotAt(@NotNull SimpleLocation location) { + public WorldPot removePotAt(@NotNull SimpleLocation location) { CWorld cWorld = loadedWorlds.get(location.getWorldName()); if (cWorld == null) { LogUtils.warn("Unsupported operation: Removing pot from unloaded world " + location); - return; + return null; } - cWorld.removePotAt(location); + return cWorld.removePotAt(location); } @Override - public void removeCropAt(@NotNull SimpleLocation location) { + public WorldCrop removeCropAt(@NotNull SimpleLocation location) { CWorld cWorld = loadedWorlds.get(location.getWorldName()); if (cWorld == null) { LogUtils.warn("Unsupported operation: Removing crop from unloaded world " + location); - return; + return null; } - cWorld.removeCropAt(location); + return cWorld.removeCropAt(location); } @Override - public void removeGlassAt(@NotNull SimpleLocation location) { + public WorldGlass removeGlassAt(@NotNull SimpleLocation location) { CWorld cWorld = loadedWorlds.get(location.getWorldName()); if (cWorld == null) { LogUtils.warn("Unsupported operation: Removing glass from unloaded world " + location); - return; + return null; } - cWorld.removeGlassAt(location); + return cWorld.removeGlassAt(location); } @Override - public void removeScarecrowAt(@NotNull SimpleLocation location) { + public WorldScarecrow removeScarecrowAt(@NotNull SimpleLocation location) { CWorld cWorld = loadedWorlds.get(location.getWorldName()); if (cWorld == null) { LogUtils.warn("Unsupported operation: Removing scarecrow from unloaded world " + location); - return; + return null; } - cWorld.removeScarecrowAt(location); + return cWorld.removeScarecrowAt(location); } @Override diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java index 9f61ea691..f7e35c777 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java @@ -35,6 +35,7 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.data.Waterlogged; +import org.jetbrains.annotations.NotNull; import java.util.Objects; @@ -95,7 +96,7 @@ public Fertilizer getFertilizer() { } @Override - public void setFertilizer(Fertilizer fertilizer) { + public void setFertilizer(@NotNull Fertilizer fertilizer) { setData("fertilizer", new StringTag("fertilizer", fertilizer.getKey())); setData("fertilizer-times", new IntTag("fertilizer-times", fertilizer.getTimes())); } @@ -122,7 +123,7 @@ public Pot getConfig() { } @Override - public void tickWater(CustomCropsChunk chunk) { + public void tickWater() { Pot pot = getConfig(); if (pot == null) { LogUtils.warn("Found a pot without config at " + getLocation() + ". Try removing the data."); diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java index d1f0825e5..856ef7da6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java @@ -8,7 +8,7 @@ public class DisplayEntityUtils { public static CRotation getRotation(Entity entity) { if (entity instanceof ItemDisplay itemDisplay) { - return RotationUtils.getCRotation(itemDisplay.getLocation().getYaw()); + return CRotation.getByYaw(itemDisplay.getLocation().getYaw()); } return CRotation.NONE; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java index abf1eb546..630876cb0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java @@ -61,39 +61,4 @@ public static Rotation getBukkitRotation(CRotation cRotation) { } } } - - public static CRotation getCRotation(Rotation rotation) { - switch (rotation) { - default -> { - return CRotation.NONE; - } - case CLOCKWISE -> { - return CRotation.WEST; - } - case COUNTER_CLOCKWISE -> { - return CRotation.EAST; - } - case FLIPPED -> { - return CRotation.NORTH; - } - } - } - - public static CRotation getCRotation(float yaw) { - yaw = Math.abs(yaw); - switch ((int) (yaw/90)) { - case 1 -> { - return CRotation.WEST; - } - case 2 -> { - return CRotation.NORTH; - } - case 3 -> { - return CRotation.EAST; - } - default -> { - return CRotation.SOUTH; - } - } - } } From 42dc2826816b90473fe702338135642aaf635e09 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 23 Apr 2024 02:44:15 +0800 Subject: [PATCH 023/329] [Library] Updated Zstd to 1.5.6-2 --- plugin/build.gradle.kts | 2 +- .../customcrops/libraries/dependencies/Dependency.java | 2 +- .../customcrops/libraries/dependencies/DependencyRegistry.java | 2 +- .../libraries/dependencies/classloader/IsolatedClassLoader.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 01d08dd69..44cb1fea0 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -59,7 +59,7 @@ dependencies { compileOnly("de.tr7zw:item-nbt-api:2.12.3") compileOnly("org.bstats:bstats-bukkit:3.0.2") implementation("com.flowpowered:flow-nbt:2.0.2") - implementation("com.github.luben:zstd-jni:1.5.5-11") + implementation("com.github.luben:zstd-jni:1.5.6-2") } tasks { diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 7b9e28909..73058fc5c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -35,7 +35,7 @@ import java.util.Locale; /** - * The dependencies used by LuckPerms. + * The dependencies used by CustomCrops. */ public enum Dependency { diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java index 2a0c5432d..e2714fc7d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java @@ -28,7 +28,7 @@ import com.google.gson.JsonElement; /** - * Applies LuckPerms specific behaviour for {@link Dependency}s. + * Applies CustomCrops specific behaviour for {@link Dependency}s. */ public class DependencyRegistry { diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java index 4538d6d98..ad4c9624e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java @@ -31,7 +31,7 @@ /** * A classloader "isolated" from the rest of the Minecraft server. * - *

Used to load specific LuckPerms dependencies without causing conflicts + *

Used to load specific CustomCrops dependencies without causing conflicts * with other plugins, or libraries provided by the server implementation.

*/ public class IsolatedClassLoader extends URLClassLoader { From 8a279bfefd1d4b140a5348cc8045e87e73f7b1d8 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 23 Apr 2024 03:44:31 +0800 Subject: [PATCH 024/329] [Update] Update adventure to 4.16.0 --- plugin/build.gradle.kts | 6 +++--- .../libraries/dependencies/Dependency.java | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 44cb1fea0..374eb5638 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -49,13 +49,13 @@ dependencies { implementation(project(":api")) implementation(project(":legacy-api")) - implementation("net.kyori:adventure-api:4.15.0") + implementation("net.kyori:adventure-api:4.16.0") implementation("net.kyori:adventure-platform-bukkit:4.3.2") implementation("com.github.Xiao-MoMi:AntiGriefLib:0.11") implementation("com.github.Xiao-MoMi:BiomeAPI:0.3") - compileOnly("net.kyori:adventure-text-minimessage:4.15.0") - compileOnly("net.kyori:adventure-text-serializer-legacy:4.15.0") + compileOnly("net.kyori:adventure-text-minimessage:4.16.0") + compileOnly("net.kyori:adventure-text-serializer-legacy:4.16.0") compileOnly("de.tr7zw:item-nbt-api:2.12.3") compileOnly("org.bstats:bstats-bukkit:3.0.2") implementation("com.flowpowered:flow-nbt:2.0.2") diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 73058fc5c..3f4e72879 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -63,7 +63,7 @@ public enum Dependency { ADVENTURE_API( "net{}kyori", "adventure-api", - "4.15.0", + "4.16.0", null, "adventure-api", Relocation.of("adventure", "net{}kyori{}adventure") @@ -71,7 +71,7 @@ public enum Dependency { ADVENTURE_KEY( "net{}kyori", "adventure-key", - "4.15.0", + "4.16.0", null, "adventure-key", Relocation.of("adventure", "net{}kyori{}adventure") @@ -79,7 +79,7 @@ public enum Dependency { ADVENTURE_NBT( "net{}kyori", "adventure-nbt", - "4.15.0", + "4.16.0", null, "adventure-nbt", Relocation.of("adventure", "net{}kyori{}adventure") @@ -87,7 +87,7 @@ public enum Dependency { ADVENTURE_LEGACY_SERIALIZER( "net{}kyori", "adventure-text-serializer-legacy", - "4.15.0", + "4.16.0", null, "adventure-text-serializer-legacy", Relocation.of("adventure", "net{}kyori{}adventure") @@ -95,7 +95,7 @@ public enum Dependency { ADVENTURE_TEXT_LOGGER( "net{}kyori", "adventure-text-logger-slf4j", - "4.15.0", + "4.16.0", null, "adventure-text-logger-slf4j", Relocation.of("adventure", "net{}kyori{}adventure") @@ -103,7 +103,7 @@ public enum Dependency { ADVENTURE_GSON( "net{}kyori", "adventure-text-serializer-gson", - "4.15.0", + "4.16.0", null, "adventure-text-serializer-gson", Relocation.of("adventure", "net{}kyori{}adventure") @@ -111,7 +111,7 @@ public enum Dependency { ADVENTURE_GSON_LEGACY( "net{}kyori", "adventure-text-serializer-gson-legacy-impl", - "4.15.0", + "4.16.0", null, "adventure-text-serializer-gson-legacy-impl", Relocation.of("adventure", "net{}kyori{}adventure") @@ -143,7 +143,7 @@ public enum Dependency { ADVENTURE_TEXT_MINIMESSAGE( "net{}kyori", "adventure-text-minimessage", - "4.15.0", + "4.16.0", null, "adventure-text-minimessage", Relocation.of("adventure", "net{}kyori{}adventure") From 38d2ede3c017ebf92825b7ed32ad9853b1900ea6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Fri, 26 Apr 2024 17:39:20 +0800 Subject: [PATCH 025/329] Fix appearance --- build.gradle.kts | 2 +- .../customcrops/mechanic/item/impl/WateringCanConfig.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7909f6af9..eeb15e05b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.5" + project.version = "3.4.5.1" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java index 9a5edcae2..fcd7b2a33 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java @@ -163,6 +163,9 @@ public void updateItem(Player player, ItemStack itemStack, int water, Map Date: Fri, 26 Apr 2024 23:17:22 +0800 Subject: [PATCH 026/329] [Fix] Possible language error --- .../customcrops/manager/MessageManagerImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java index a273b2202..5b90e30a1 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java @@ -22,6 +22,7 @@ import net.momirealms.customcrops.api.manager.ConfigManager; import net.momirealms.customcrops.api.manager.MessageManager; import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; @@ -45,7 +46,13 @@ public MessageManagerImpl(CustomCropsPlugin plugin) { @Override public void load() { - YamlConfiguration config = ConfigUtils.getConfig("messages" + File.separator + ConfigManager.lang() + ".yml"); + YamlConfiguration config; + try { + config = ConfigUtils.getConfig("messages" + File.separator + ConfigManager.lang() + ".yml"); + } catch (Exception e) { + LogUtils.warn(ConfigManager.lang() + ".yml doesn't exist. Using the default language file now."); + config = ConfigUtils.getConfig("messages" + File.separator + "en" + ".yml"); + } ConfigurationSection section = config.getConfigurationSection("messages"); if (section != null) { prefix = section.getString("prefix", "[CustomCrops] "); From 2521e4bc22e114db2b145a9c41172695c3a3497f Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 27 Apr 2024 20:33:25 +0800 Subject: [PATCH 027/329] Fix default configs --- build.gradle.kts | 2 +- plugin/src/main/resources/contents/sprinklers/default.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index eeb15e05b..57e31e52d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.5.1" + project.version = "3.4.5.2" apply() apply(plugin = "java") diff --git a/plugin/src/main/resources/contents/sprinklers/default.yml b/plugin/src/main/resources/contents/sprinklers/default.yml index 2a277fc14..072c8ca5b 100644 --- a/plugin/src/main/resources/contents/sprinklers/default.yml +++ b/plugin/src/main/resources/contents/sprinklers/default.yml @@ -77,7 +77,7 @@ sprinkler_1: fake_item_action: type: fake-item value: - item: customcrops:water_effect + item: {0}water_effect duration: 100 x: 0.5 y: 0.4 @@ -163,7 +163,7 @@ sprinkler_2: fake_item_action: type: fake-item value: - item: customcrops:water_effect + item: {0}water_effect duration: 100 x: 0.5 y: 0.4 @@ -251,7 +251,7 @@ sprinkler_3: fake_item_action: type: fake-item value: - item: customcrops:water_effect + item: {0}water_effect duration: 100 x: 0.5 y: 0.4 From e3b21885075ca6a1cf2dd8bffda34dbe27f85311 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 27 Apr 2024 22:24:48 +0800 Subject: [PATCH 028/329] [Compatibility] Readd quest compatibilities --- build.gradle.kts | 2 +- .../compatibility/IntegrationManagerImpl.java | 17 ++ .../compatibility/quest/BattlePassHook.java | 87 ++++++++ .../compatibility/quest/BetonQuestHook.java | 190 ++++++++++++++++++ .../compatibility/quest/ClueScrollsHook.java | 67 ++++++ 5 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BetonQuestHook.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/ClueScrollsHook.java diff --git a/build.gradle.kts b/build.gradle.kts index 57e31e52d..c3c9cc2e3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.5.2" + project.version = "3.4.6" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java index 1243e73ec..fefb4edb9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java @@ -27,6 +27,9 @@ import net.momirealms.customcrops.compatibility.item.NeigeItemsItemImpl; import net.momirealms.customcrops.compatibility.item.ZaphkielItemImpl; import net.momirealms.customcrops.compatibility.level.*; +import net.momirealms.customcrops.compatibility.quest.BattlePassHook; +import net.momirealms.customcrops.compatibility.quest.BetonQuestHook; +import net.momirealms.customcrops.compatibility.quest.ClueScrollsHook; import net.momirealms.customcrops.compatibility.season.AdvancedSeasonsImpl; import net.momirealms.customcrops.compatibility.season.InBuiltSeason; import net.momirealms.customcrops.compatibility.season.RealisticSeasonsImpl; @@ -91,6 +94,20 @@ public void init() { registerLevelPlugin("AuraSkills", new AuraSkillsImpl()); hookMessage("AuraSkills"); } + if (plugin.isHookedPluginEnabled("BattlePass")){ + BattlePassHook battlePassHook = new BattlePassHook(); + battlePassHook.register(); + hookMessage("BattlePass"); + } + if (plugin.isHookedPluginEnabled("ClueScrolls")) { + ClueScrollsHook clueScrollsHook = new ClueScrollsHook(); + clueScrollsHook.register(); + hookMessage("ClueScrolls"); + } + if (plugin.isHookedPluginEnabled("BetonQuest")) { + BetonQuestHook.register(); + hookMessage("BetonQuest"); + } if (plugin.isHookedPluginEnabled("RealisticSeasons")) { this.seasonInterface = new RealisticSeasonsImpl(); hookMessage("RealisticSeasons"); diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java new file mode 100644 index 000000000..b0b6c908e --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) <2022> + * + * 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.compatibility.quest; + +import io.github.battlepass.BattlePlugin; +import io.github.battlepass.api.events.server.PluginReloadEvent; +import net.advancedplugins.bp.impl.actions.ActionRegistry; +import net.advancedplugins.bp.impl.actions.external.executor.ActionQuestExecutor; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.event.CropBreakEvent; +import net.momirealms.customcrops.api.event.CropPlantEvent; +import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.plugin.java.JavaPlugin; + +public class BattlePassHook implements Listener { + + public BattlePassHook() { + Bukkit.getPluginManager().registerEvents(this, CustomCropsPlugin.get()); + } + + public void register() { + ActionRegistry actionRegistry = BattlePlugin.getPlugin().getActionRegistry(); + actionRegistry.hook("customcrops", BPHarvestCropsQuest::new); + } + + @EventHandler(ignoreCancelled = true) + public void onBattlePassReload(PluginReloadEvent event){ + register(); + } + + private static class BPHarvestCropsQuest extends ActionQuestExecutor { + public BPHarvestCropsQuest(JavaPlugin plugin) { + super(plugin, "customcrops"); + } + + @EventHandler (ignoreCancelled = true) + public void onBreakCrop(CropBreakEvent event){ + Player player = event.getPlayer(); + if (player == null) return; + + WorldCrop worldCrop = event.getWorldCrop(); + if (worldCrop == null) return; + String id = worldCrop.getConfig().getStageItemByPoint(worldCrop.getPoint()); + + // Harvest crops + this.executionBuilder("harvest") + .player(player) + .root(id) + .progress(1) + .buildAndExecute(); + } + + @EventHandler (ignoreCancelled = true) + public void onPlantCrop(CropPlantEvent event){ + Player player = event.getPlayer(); + + Crop crop = event.getCrop(); + + // Harvest crops + this.executionBuilder("plant") + .player(player) + .root(crop.getKey()) + .progress(1) + .buildAndExecute(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BetonQuestHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BetonQuestHook.java new file mode 100644 index 000000000..c9c56df4f --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BetonQuestHook.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) <2022> + * + * 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.compatibility.quest; + +import net.momirealms.customcrops.api.event.CropBreakEvent; +import net.momirealms.customcrops.api.event.CropPlantEvent; +import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.util.LogUtils; +import org.betonquest.betonquest.BetonQuest; +import org.betonquest.betonquest.Instruction; +import org.betonquest.betonquest.VariableNumber; +import org.betonquest.betonquest.api.CountingObjective; +import org.betonquest.betonquest.api.config.quest.QuestPackage; +import org.betonquest.betonquest.api.profiles.OnlineProfile; +import org.betonquest.betonquest.api.profiles.Profile; +import org.betonquest.betonquest.exceptions.InstructionParseException; +import org.betonquest.betonquest.utils.PlayerConverter; +import org.betonquest.betonquest.utils.location.CompoundLocation; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; + +import java.util.Collections; +import java.util.HashSet; + +@SuppressWarnings("DuplicatedCode") +public class BetonQuestHook { + + public static void register() { + BetonQuest.getInstance().registerObjectives("customcrops_harvest", HarvestObjective.class); + BetonQuest.getInstance().registerObjectives("customcrops_plant", PlantObjective.class); + } + + public static class HarvestObjective extends CountingObjective implements Listener { + + private final CompoundLocation playerLocation; + private final VariableNumber rangeVar; + private final HashSet crop_ids; + + public HarvestObjective(Instruction instruction) throws InstructionParseException { + super(instruction, "crop_to_harvest"); + crop_ids = new HashSet<>(); + Collections.addAll(crop_ids, instruction.getArray()); + targetAmount = instruction.getVarNum(); + preCheckAmountNotLessThanOne(targetAmount); + final QuestPackage pack = instruction.getPackage(); + final String loc = instruction.getOptional("playerLocation"); + final String range = instruction.getOptional("range"); + if (loc != null && range != null) { + playerLocation = new CompoundLocation(pack, loc); + rangeVar = new VariableNumber(pack, range); + } else { + playerLocation = null; + rangeVar = null; + } + } + + @EventHandler (ignoreCancelled = true) + public void onBreakCrop(CropBreakEvent event) { + if (event.getPlayer() == null) { + return; + } + WorldCrop crop = event.getWorldCrop(); + if (crop == null) return; + + OnlineProfile onlineProfile = PlayerConverter.getID(event.getPlayer()); + if (!containsPlayer(onlineProfile)) { + return; + } + if (isInvalidLocation(event, onlineProfile)) { + return; + } + if (this.crop_ids.contains(crop.getConfig().getStageItemByPoint(crop.getPoint())) && this.checkConditions(onlineProfile)) { + getCountingData(onlineProfile).progress(1); + completeIfDoneOrNotify(onlineProfile); + } + } + + private boolean isInvalidLocation(CropBreakEvent event, final Profile profile) { + if (playerLocation == null || rangeVar == null) { + return false; + } + + final Location targetLocation; + try { + targetLocation = playerLocation.getLocation(profile); + } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { + LogUtils.warn(e.getMessage()); + return true; + } + final int range = rangeVar.getInt(profile); + final Location playerLoc = event.getPlayer().getLocation(); + return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range; + } + + @Override + public void start() { + Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance()); + } + + @Override + public void stop() { + HandlerList.unregisterAll(this); + } + } + + public static class PlantObjective extends CountingObjective implements Listener { + + private final CompoundLocation playerLocation; + private final VariableNumber rangeVar; + private final HashSet loot_groups; + + public PlantObjective(Instruction instruction) throws InstructionParseException { + super(instruction, "crop_to_plant"); + loot_groups = new HashSet<>(); + Collections.addAll(loot_groups, instruction.getArray()); + targetAmount = instruction.getVarNum(); + preCheckAmountNotLessThanOne(targetAmount); + final QuestPackage pack = instruction.getPackage(); + final String loc = instruction.getOptional("playerLocation"); + final String range = instruction.getOptional("range"); + if (loc != null && range != null) { + playerLocation = new CompoundLocation(pack, loc); + rangeVar = new VariableNumber(pack, range); + } else { + playerLocation = null; + rangeVar = null; + } + } + + @EventHandler (ignoreCancelled = true) + public void onPlantCrop(CropPlantEvent event) { + OnlineProfile onlineProfile = PlayerConverter.getID(event.getPlayer()); + if (!containsPlayer(onlineProfile)) { + return; + } + if (isInvalidLocation(event, onlineProfile)) { + return; + } + if (this.loot_groups.contains(event.getCrop().getKey()) && this.checkConditions(onlineProfile)) { + getCountingData(onlineProfile).progress(1); + completeIfDoneOrNotify(onlineProfile); + } + } + + private boolean isInvalidLocation(CropPlantEvent event, final Profile profile) { + if (playerLocation == null || rangeVar == null) { + return false; + } + + final Location targetLocation; + try { + targetLocation = playerLocation.getLocation(profile); + } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { + LogUtils.warn(e.getMessage()); + return true; + } + final int range = rangeVar.getInt(profile); + final Location playerLoc = event.getPlayer().getLocation(); + return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range; + } + + @Override + public void start() { + Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance()); + } + + @Override + public void stop() { + HandlerList.unregisterAll(this); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/ClueScrollsHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/ClueScrollsHook.java new file mode 100644 index 000000000..997e37914 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/ClueScrollsHook.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) <2022> + * + * 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.compatibility.quest; + +import com.electro2560.dev.cluescrolls.api.*; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.event.CropBreakEvent; +import net.momirealms.customcrops.api.event.CropPlantEvent; +import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class ClueScrollsHook implements Listener { + + private final CustomClue harvestClue; + private final CustomClue plantClue; + + public ClueScrollsHook() { + harvestClue = ClueScrollsAPI.getInstance().registerCustomClue(CustomCropsPlugin.getInstance(), "harvest", new ClueConfigData("id", DataType.STRING)); + plantClue = ClueScrollsAPI.getInstance().registerCustomClue(CustomCropsPlugin.getInstance(), "plant", new ClueConfigData("id", DataType.STRING)); + } + + public void register() { + Bukkit.getPluginManager().registerEvents(this, CustomCropsPlugin.get()); + } + + @EventHandler (ignoreCancelled = true) + public void onBreakCrop(CropBreakEvent event) { + final Player player = event.getPlayer(); + if (player == null) return; + + WorldCrop crop = event.getWorldCrop(); + if (crop == null) return; + + harvestClue.handle( + player, + 1, + new ClueDataPair("id", crop.getConfig().getStageItemByPoint(crop.getPoint())) + ); + } + + @EventHandler (ignoreCancelled = true) + public void onPlantCrop(CropPlantEvent event) { + plantClue.handle( + event.getPlayer(), + 1, + new ClueDataPair("id", event.getCrop().getKey()) + ); + } +} From d42aae28c8ce4e0fccbc85f5c20f764ab35e002d Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 27 Apr 2024 22:44:16 +0800 Subject: [PATCH 029/329] [Feature] Variation keeps the data --- .../net/momirealms/customcrops/api/mechanic/item/Crop.java | 7 +++++++ .../customcrops/mechanic/action/ActionManagerImpl.java | 5 ++++- .../customcrops/mechanic/item/impl/CropConfig.java | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java index e378a98da..4a942f1e0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java @@ -151,6 +151,13 @@ public interface Crop extends KeyItem { interface Stage { + /** + * Get the crop config + * + * @return crop config + */ + Crop getCrop(); + /** * Get the offset of the hologram * diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index b3f4501d7..c34e1ade5 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -494,9 +494,12 @@ private void registerVariationAction() { } for (VariationCrop variationCrop : variations) { if (Math.random() < variationCrop.getChance() + bonus) { + SimpleLocation location = SimpleLocation.of(state.getLocation()); plugin.getItemManager().removeAnythingAt(state.getLocation()); - plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(state.getLocation())); + plugin.getWorldManager().removeAnythingAt(location); plugin.getItemManager().placeItem(state.getLocation(), variationCrop.getItemCarrier(), variationCrop.getItemID()); + Optional.ofNullable(plugin.getItemManager().getCropStageByStageID(variationCrop.getItemID())) + .ifPresent(stage -> plugin.getWorldManager().addCropAt(new MemoryCrop(location, stage.getCrop().getKey(), stage.getPoint()), location)); break; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java index 2b746fe63..2618d5a02 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.mechanic.item.impl; +import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.mechanic.action.Action; import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; import net.momirealms.customcrops.api.mechanic.condition.Conditions; @@ -206,6 +207,11 @@ public CropStageConfig( this.breakRequirements = breakRequirements; } + @Override + public Crop getCrop() { + return CustomCropsPlugin.get().getItemManager().getCropByStageID(stageID); + } + @Override public double getHologramOffset() { return hologramOffset; From e750d40cf6072ab7f507cf506809b063328467ad Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 27 Apr 2024 22:57:46 +0800 Subject: [PATCH 030/329] [Relocate] Relocated Adventure and some libs --- plugin/build.gradle.kts | 8 +- .../customcrops/CustomCropsPluginImpl.java | 25 +++-- .../libraries/dependencies/Dependency.java | 103 +++--------------- .../mechanic/item/ItemManagerImpl.java | 1 - 4 files changed, 30 insertions(+), 107 deletions(-) diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 374eb5638..4a5a44150 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -49,10 +49,10 @@ dependencies { implementation(project(":api")) implementation(project(":legacy-api")) - implementation("net.kyori:adventure-api:4.16.0") - implementation("net.kyori:adventure-platform-bukkit:4.3.2") - implementation("com.github.Xiao-MoMi:AntiGriefLib:0.11") - implementation("com.github.Xiao-MoMi:BiomeAPI:0.3") + compileOnly("net.kyori:adventure-api:4.16.0") + compileOnly("net.kyori:adventure-platform-bukkit:4.3.2") + compileOnly("com.github.Xiao-MoMi:AntiGriefLib:0.11") + compileOnly("com.github.Xiao-MoMi:BiomeAPI:0.3") compileOnly("net.kyori:adventure-text-minimessage:4.16.0") compileOnly("net.kyori:adventure-text-serializer-legacy:4.16.0") diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index 3d7e1ede6..c05aa0395 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -61,11 +61,12 @@ public void onLoad() { Dependency.GSON, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, + Dependency.ADVENTURE_API, Dependency.COMMAND_API, Dependency.NBT_API, Dependency.BOOSTED_YAML, - Dependency.ADVENTURE_TEXT_MINIMESSAGE, - Dependency.ADVENTURE_LEGACY_SERIALIZER, + Dependency.BIOME_API, + Dependency.ANTI_GRIEF, Dependency.BSTATS_BASE, Dependency.BSTATS_BUKKIT ) @@ -112,16 +113,16 @@ public void onEnable() { @Override public void onDisable() { - this.commandManager.disable(); - this.adventure.disable(); - this.requirementManager.disable(); - this.actionManager.disable(); - this.worldManager.disable(); - this.itemManager.disable(); - this.conditionManager.disable(); - this.coolDownManager.disable(); - this.placeholderManager.disable(); - ((SchedulerImpl) scheduler).shutdown(); + if (this.commandManager != null) this.commandManager.disable(); + if (this.adventure != null) this.adventure.disable(); + if (this.requirementManager != null) this.requirementManager.disable(); + if (this.actionManager != null) this.actionManager.disable(); + if (this.worldManager != null) this.worldManager.disable(); + if (this.itemManager != null) this.itemManager.disable(); + if (this.conditionManager != null) this.conditionManager.disable(); + if (this.coolDownManager != null) this.coolDownManager.disable(); + if (this.placeholderManager != null) this.placeholderManager.disable(); + if (this.scheduler != null) ((SchedulerImpl) scheduler).shutdown(); instance = null; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 3f4e72879..96c0e3b9b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -60,94 +60,7 @@ public enum Dependency { null, "jar-relocator" ), - ADVENTURE_API( - "net{}kyori", - "adventure-api", - "4.16.0", - null, - "adventure-api", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_KEY( - "net{}kyori", - "adventure-key", - "4.16.0", - null, - "adventure-key", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_NBT( - "net{}kyori", - "adventure-nbt", - "4.16.0", - null, - "adventure-nbt", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_LEGACY_SERIALIZER( - "net{}kyori", - "adventure-text-serializer-legacy", - "4.16.0", - null, - "adventure-text-serializer-legacy", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_TEXT_LOGGER( - "net{}kyori", - "adventure-text-logger-slf4j", - "4.16.0", - null, - "adventure-text-logger-slf4j", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_GSON( - "net{}kyori", - "adventure-text-serializer-gson", - "4.16.0", - null, - "adventure-text-serializer-gson", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_GSON_LEGACY( - "net{}kyori", - "adventure-text-serializer-gson-legacy-impl", - "4.16.0", - null, - "adventure-text-serializer-gson-legacy-impl", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_PLATFORM( - "net{}kyori", - "adventure-platform-api", - "4.3.2", - null, - "adventure-platform-api", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_PLATFORM_BUKKIT( - "net{}kyori", - "adventure-platform-bukkit", - "4.3.2", - null, - "adventure-platform-bukkit", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_PLATFORM_FACET( - "net{}kyori", - "adventure-platform-facet", - "4.3.2", - null, - "adventure-platform-facet", - Relocation.of("adventure", "net{}kyori{}adventure") - ), - ADVENTURE_TEXT_MINIMESSAGE( - "net{}kyori", - "adventure-text-minimessage", - "4.16.0", - null, - "adventure-text-minimessage", - Relocation.of("adventure", "net{}kyori{}adventure") - ), + COMMAND_API( "dev{}jorel", "commandapi-bukkit-shade", @@ -164,6 +77,16 @@ public enum Dependency { "boosted-yaml", Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") ), + ADVENTURE_API( + "com.github.Xiao-MoMi", + "Adventure-Bundle", + "4.16.0", + "jitpack", + "adventure-bundle", + Relocation.of("adventure", "net{}kyori{}adventure"), + Relocation.of("option", "net{}kyori{}option"), + Relocation.of("examination", "net{}kyori{}examination") + ), H2_DRIVER( "com.h2database", "h2", @@ -203,7 +126,7 @@ public enum Dependency { ANTI_GRIEF( "com{}github{}Xiao-MoMi", "AntiGriefLib", - "0.7", + "0.11", "jitpack", "antigrief-lib", Relocation.of("antigrieflib", "net{}momirealms{}antigrieflib") @@ -211,7 +134,7 @@ public enum Dependency { BIOME_API( "com{}github{}Xiao-MoMi", "BiomeAPI", - "0.2", + "0.3", "jitpack", "biome-api", Relocation.of("biomeapi", "net{}momirealms{}biomeapi") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index ef108f14a..24716769d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -140,7 +140,6 @@ public ItemManagerImpl(CustomCropsPlugin plugin, AntiGriefLib antiGriefLib) { LogUtils.severe(" ItemsAdder: https://www.spigotmc.org/resources/73355/"); LogUtils.severe(" Oraxen: https://www.spigotmc.org/resources/72448/"); LogUtils.severe("======================================================"); - Bukkit.getPluginManager().disablePlugin(plugin); } } From 9f379ec601c85812ab0aa65b9a10eec81386dd89 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 29 Apr 2024 14:35:29 +0800 Subject: [PATCH 031/329] [Compatibility] Oraxen 2.0 support --- .../customcrops/api/manager/ItemManager.java | 50 ++++++++ .../item}/AbstractCustomListener.java | 11 +- .../api}/mechanic/item/CustomProvider.java | 9 +- .../api}/util/DisplayEntityUtils.java | 2 +- .../customcrops/api}/util/EventUtils.java | 2 +- .../customcrops/api/util/StringUtils.java | 15 +++ build.gradle.kts | 3 +- oraxen-legacy/.gitignore | 42 +++++++ oraxen-legacy/build.gradle.kts | 5 + .../oraxenlegacy/LegacyOraxenListener.java | 108 ++++++++++++++++++ .../oraxenlegacy/LegacyOraxenProvider.java | 104 +++++++++++++++++ plugin/build.gradle.kts | 3 +- .../customcrops/CustomCropsPluginImpl.java | 2 +- .../mechanic/action/ActionManagerImpl.java | 2 +- .../mechanic/item/ItemManagerImpl.java | 24 +++- .../custom/crucible/CrucibleListener.java | 2 +- .../custom/crucible/CrucibleProvider.java | 2 +- .../custom/itemsadder/ItemsAdderListener.java | 2 +- .../custom/itemsadder/ItemsAdderProvider.java | 2 +- .../item/custom/oraxen/OraxenListener.java | 18 +-- .../item/custom/oraxen/OraxenProvider.java | 4 +- .../customcrops/mechanic/world/CWorld.java | 2 +- settings.gradle | 1 + 23 files changed, 378 insertions(+), 37 deletions(-) rename {plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom => api/src/main/java/net/momirealms/customcrops/api/mechanic/item}/AbstractCustomListener.java (97%) rename {plugin/src/main/java/net/momirealms/customcrops => api/src/main/java/net/momirealms/customcrops/api}/mechanic/item/CustomProvider.java (95%) rename {plugin/src/main/java/net/momirealms/customcrops => api/src/main/java/net/momirealms/customcrops/api}/util/DisplayEntityUtils.java (90%) rename {plugin/src/main/java/net/momirealms/customcrops => api/src/main/java/net/momirealms/customcrops/api}/util/EventUtils.java (96%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java create mode 100644 oraxen-legacy/.gitignore create mode 100644 oraxen-legacy/build.gradle.kts create mode 100644 oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java create mode 100644 oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java index d0e7d16bd..9ba33b6ed 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java @@ -23,13 +23,16 @@ import net.momirealms.customcrops.api.mechanic.misc.CRotation; import org.bukkit.Location; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.List; public interface ItemManager extends Reloadable { @@ -325,6 +328,53 @@ public interface ItemManager extends Reloadable { @Nullable Crop.Stage getCropStageByStageID(String id); + void handlePlayerInteractBlock( + Player player, + Block clickedBlock, + BlockFace clickedFace, + Cancellable event + ); + + void handlePlayerInteractAir( + Player player, + Cancellable event + ); + + void handlePlayerBreakBlock( + Player player, + Block brokenBlock, + String blockID, + Cancellable event + ); + + void handlePlayerInteractFurniture( + Player player, + Location location, + String id, + Entity baseEntity, + Cancellable event + ); + + void handlePlayerPlaceFurniture( + Player player, + Location location, + String id, + Cancellable event + ); + + void handlePlayerBreakFurniture( + Player player, + Location location, + String id, + Cancellable event + ); + + void handlePlayerPlaceBlock(Player player, Block block, String blockID, Cancellable event); + + void handleEntityTramplingBlock(Entity entity, Block block, Cancellable event); + + void handleExplosion(Entity entity, List blocks, Cancellable event); + /** * Update a pot's block state * diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/AbstractCustomListener.java similarity index 97% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/AbstractCustomListener.java index 76f25842c..f748704ab 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/AbstractCustomListener.java @@ -15,19 +15,18 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.item.custom; +package net.momirealms.customcrops.api.mechanic.item; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.event.BoneMealDispenseEvent; import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.ItemManager; import net.momirealms.customcrops.api.manager.VersionManager; import net.momirealms.customcrops.api.manager.WorldManager; -import net.momirealms.customcrops.api.mechanic.item.*; import net.momirealms.customcrops.api.mechanic.requirement.State; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.util.EventUtils; +import net.momirealms.customcrops.api.util.EventUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -56,10 +55,10 @@ public abstract class AbstractCustomListener implements Listener { - protected ItemManagerImpl itemManager; + protected ItemManager itemManager; private final HashSet CUSTOM_MATERIAL = new HashSet<>(); - public AbstractCustomListener(ItemManagerImpl itemManager) { + public AbstractCustomListener(ItemManager itemManager) { this.itemManager = itemManager; this.CUSTOM_MATERIAL.addAll( List.of( diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/CustomProvider.java similarity index 95% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/CustomProvider.java index ce1f80a18..20f99b253 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/CustomProvider.java @@ -15,14 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.item; +package net.momirealms.customcrops.api.mechanic.item; import net.momirealms.customcrops.api.manager.VersionManager; import net.momirealms.customcrops.api.mechanic.misc.CRotation; +import net.momirealms.customcrops.api.util.DisplayEntityUtils; import net.momirealms.customcrops.api.util.LocationUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.customcrops.util.DisplayEntityUtils; -import net.momirealms.customcrops.util.RotationUtils; +import net.momirealms.customcrops.api.util.StringUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -38,7 +37,7 @@ public interface CustomProvider { void placeCustomBlock(Location location, String id); default void placeBlock(Location location, String id) { - if (ConfigUtils.isVanillaItem(id)) { + if (StringUtils.isCapitalLetter(id)) { location.getBlock().setType(Material.valueOf(id)); } else { placeCustomBlock(location, id); diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/DisplayEntityUtils.java similarity index 90% rename from plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java rename to api/src/main/java/net/momirealms/customcrops/api/util/DisplayEntityUtils.java index 856ef7da6..fcef9608c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/DisplayEntityUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/DisplayEntityUtils.java @@ -1,4 +1,4 @@ -package net.momirealms.customcrops.util; +package net.momirealms.customcrops.api.util; import net.momirealms.customcrops.api.mechanic.misc.CRotation; import org.bukkit.entity.Entity; diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/EventUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/EventUtils.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/util/EventUtils.java rename to api/src/main/java/net/momirealms/customcrops/api/util/EventUtils.java index 2f503b95e..038de4d5a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/EventUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/EventUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.util; +package net.momirealms.customcrops.api.util; import org.bukkit.Bukkit; import org.bukkit.event.Cancellable; diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java new file mode 100644 index 000000000..65e1687bc --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java @@ -0,0 +1,15 @@ +package net.momirealms.customcrops.api.util; + +public class StringUtils { + + public static boolean isCapitalLetter(String item) { + char[] chars = item.toCharArray(); + for (char character : chars) { + if ((character < 65 || character > 90) && character != 95) { + return false; + } + } + return true; + } + +} diff --git a/build.gradle.kts b/build.gradle.kts index c3c9cc2e3..c64a74921 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.6" + project.version = "3.4.7-BETA" apply() apply(plugin = "java") @@ -44,6 +44,7 @@ allprojects { maven("https://repo.rapture.pw/repository/maven-releases/") maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") maven("https://repo.xenondevs.xyz/releases/") + maven("https://repo.oraxen.com/snapshots/") } } diff --git a/oraxen-legacy/.gitignore b/oraxen-legacy/.gitignore new file mode 100644 index 000000000..b63da4551 --- /dev/null +++ b/oraxen-legacy/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/oraxen-legacy/build.gradle.kts b/oraxen-legacy/build.gradle.kts new file mode 100644 index 000000000..a9bc7a9af --- /dev/null +++ b/oraxen-legacy/build.gradle.kts @@ -0,0 +1,5 @@ +dependencies { + compileOnly(project(":api")) + compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT") + compileOnly("com.github.oraxen:oraxen:1.172.0") +} \ No newline at end of file diff --git a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java new file mode 100644 index 000000000..58a027089 --- /dev/null +++ b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) <2022> + * + * 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.mechanic.item.custom.oraxenlegacy; + +import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; +import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockBreakEvent; +import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockPlaceEvent; +import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockBreakEvent; +import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockPlaceEvent; +import net.momirealms.customcrops.api.manager.ItemManager; +import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; +import net.momirealms.customcrops.api.util.LocationUtils; +import org.bukkit.event.EventHandler; + +public class LegacyOraxenListener extends AbstractCustomListener { + + public LegacyOraxenListener(ItemManager itemManager) { + super(itemManager); + } + + @EventHandler (ignoreCancelled = true) + public void onBreakCustomNoteBlock(OraxenNoteBlockBreakEvent event) { + this.itemManager.handlePlayerBreakBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onBreakCustomStringBlock(OraxenStringBlockBreakEvent event) { + this.itemManager.handlePlayerBreakBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceCustomBlock(OraxenNoteBlockPlaceEvent event) { + super.onPlaceBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceCustomBlock(OraxenStringBlockPlaceEvent event) { + super.onPlaceBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceFurniture(OraxenFurniturePlaceEvent event) { + super.onPlaceFurniture( + event.getPlayer(), + event.getBlock().getLocation(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onBreakFurniture(OraxenFurnitureBreakEvent event) { + super.onBreakFurniture( + event.getPlayer(), + LocationUtils.toBlockLocation(event.getBaseEntity().getLocation()), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onInteractFurniture(OraxenFurnitureInteractEvent event) { + super.onInteractFurniture( + event.getPlayer(), + LocationUtils.toBlockLocation(event.getBaseEntity().getLocation()), + event.getMechanic().getItemID(), + event.getBaseEntity(), + event + ); + } +} diff --git a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java new file mode 100644 index 000000000..8d3be1a9b --- /dev/null +++ b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) <2022> + * + * 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.mechanic.item.custom.oraxenlegacy; + +import io.th0rgal.oraxen.api.OraxenBlocks; +import io.th0rgal.oraxen.api.OraxenFurniture; +import io.th0rgal.oraxen.api.OraxenItems; +import io.th0rgal.oraxen.items.ItemBuilder; +import io.th0rgal.oraxen.mechanics.Mechanic; +import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; +import net.momirealms.customcrops.api.mechanic.item.CustomProvider; +import net.momirealms.customcrops.api.util.LogUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Rotation; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.ItemStack; + +public class LegacyOraxenProvider implements CustomProvider { + + @Override + public boolean removeBlock(Location location) { + Block block = location.getBlock(); + if (block.getType() == Material.AIR) { + return false; + } + block.setType(Material.AIR); + return true; + } + + @Override + public void placeCustomBlock(Location location, String id) { + OraxenBlocks.place(id, location); + } + + @Override + public Entity placeFurniture(Location location, String id) { + Entity entity = OraxenFurniture.place(id, location, Rotation.NONE, BlockFace.UP); + if (entity == null) { + LogUtils.warn("Furniture(" + id +") doesn't exist in Oraxen configs. Please double check if that furniture exists."); + } + return entity; + } + + @Override + public void removeFurniture(Entity entity) { + OraxenFurniture.remove(entity, null); + } + + @Override + public String getBlockID(Block block) { + Mechanic mechanic = OraxenBlocks.getOraxenBlock(block.getLocation()); + if (mechanic == null) { + return block.getType().name(); + } + return mechanic.getItemID(); + } + + @Override + public String getItemID(ItemStack itemStack) { + return OraxenItems.getIdByItem(itemStack); + } + + @Override + public ItemStack getItemStack(String id) { + if (id == null) return new ItemStack(Material.AIR); + ItemBuilder builder = OraxenItems.getItemById(id); + if (builder == null) { + return null; + } + return builder.build(); + } + + @Override + public String getEntityID(Entity entity) { + FurnitureMechanic mechanic = OraxenFurniture.getFurnitureMechanic(entity); + if (mechanic == null) { + return entity.getType().name(); + } + return mechanic.getItemID(); + } + + @Override + public boolean isFurniture(Entity entity) { + return OraxenFurniture.isFurniture(entity); + } +} diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 4a5a44150..baa80beb1 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { // Items compileOnly("com.github.LoneDev6:api-itemsadder:3.6.2-beta-r3-b") - compileOnly("com.github.oraxen:oraxen:1.172.0") + compileOnly("io.th0rgal:oraxen:2.0-SNAPSHOT") compileOnly("pers.neige.neigeitems:NeigeItems:1.16.24") compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT") compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT") @@ -48,6 +48,7 @@ dependencies { compileOnly(files("libs/RealisticSeasons-api.jar")) implementation(project(":api")) + implementation(project(":oraxen-legacy")) implementation(project(":legacy-api")) compileOnly("net.kyori:adventure-api:4.16.0") compileOnly("net.kyori:adventure-platform-bukkit:4.3.2") diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index c05aa0395..4498e00af 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -38,7 +38,7 @@ import net.momirealms.customcrops.mechanic.requirement.RequirementManagerImpl; import net.momirealms.customcrops.mechanic.world.WorldManagerImpl; import net.momirealms.customcrops.scheduler.SchedulerImpl; -import net.momirealms.customcrops.util.EventUtils; +import net.momirealms.customcrops.api.util.EventUtils; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index c34e1ade5..6bdb473c2 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -58,7 +58,7 @@ import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; import net.momirealms.customcrops.util.ClassUtils; import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.customcrops.util.EventUtils; +import net.momirealms.customcrops.api.util.EventUtils; import net.momirealms.customcrops.util.ItemUtils; import org.bukkit.*; import org.bukkit.block.BlockFace; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 24716769d..0b47bb853 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -40,13 +40,15 @@ import net.momirealms.customcrops.api.mechanic.world.level.*; import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; import net.momirealms.customcrops.mechanic.item.custom.crucible.CrucibleListener; import net.momirealms.customcrops.mechanic.item.custom.crucible.CrucibleProvider; import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderListener; import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderProvider; import net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenListener; import net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenProvider; +import net.momirealms.customcrops.mechanic.item.custom.oraxenlegacy.LegacyOraxenListener; +import net.momirealms.customcrops.mechanic.item.custom.oraxenlegacy.LegacyOraxenProvider; import net.momirealms.customcrops.mechanic.item.function.CFunction; import net.momirealms.customcrops.mechanic.item.function.FunctionResult; import net.momirealms.customcrops.mechanic.item.function.FunctionTrigger; @@ -58,7 +60,7 @@ import net.momirealms.customcrops.mechanic.item.impl.fertilizer.*; import net.momirealms.customcrops.mechanic.world.block.*; import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.customcrops.util.EventUtils; +import net.momirealms.customcrops.api.util.EventUtils; import net.momirealms.customcrops.util.ItemUtils; import net.momirealms.customcrops.util.RotationUtils; import org.bukkit.*; @@ -126,8 +128,13 @@ public ItemManagerImpl(CustomCropsPlugin plugin, AntiGriefLib antiGriefLib) { this.stage2CropStageMap = new HashMap<>(); this.deadCrops = new HashSet<>(); if (Bukkit.getPluginManager().getPlugin("Oraxen") != null) { - listener = new OraxenListener(this); - customProvider = new OraxenProvider(); + if (Bukkit.getPluginManager().getPlugin("Oraxen").getDescription().getVersion().startsWith("2")) { + listener = new OraxenListener(this); + customProvider = new OraxenProvider(); + } else { + listener = new LegacyOraxenListener(this); + customProvider = new LegacyOraxenProvider(); + } } else if (Bukkit.getPluginManager().getPlugin("ItemsAdder") != null) { listener = new ItemsAdderListener(this); customProvider = new ItemsAdderProvider(); @@ -2328,6 +2335,7 @@ private void registerItemFunction(String item, FunctionTrigger trigger, CFunctio } } + @Override @SuppressWarnings("DuplicatedCode") public void handlePlayerInteractBlock( Player player, @@ -2358,6 +2366,7 @@ public void handlePlayerInteractBlock( .ifPresent(itemFunctions -> handleFunctions(itemFunctions, condition, event)); } + @Override public void handlePlayerInteractAir( Player player, Cancellable event @@ -2377,6 +2386,7 @@ public void handlePlayerInteractAir( .ifPresent(cFunctions -> handleFunctions(cFunctions, condition, event)); } + @Override public void handlePlayerBreakBlock( Player player, Block brokenBlock, @@ -2395,6 +2405,7 @@ public void handlePlayerBreakBlock( .ifPresent(cFunctions -> handleFunctions(cFunctions, new BreakBlockWrapper(player, brokenBlock), event)); } + @Override @SuppressWarnings("DuplicatedCode") public void handlePlayerInteractFurniture( Player player, @@ -2423,6 +2434,7 @@ public void handlePlayerInteractFurniture( .ifPresent(cFunctions -> handleFunctions(cFunctions, condition, event)); } + @Override public void handlePlayerPlaceFurniture( Player player, Location location, @@ -2442,6 +2454,7 @@ public void handlePlayerPlaceFurniture( .ifPresent(cFunctions -> handleFunctions(cFunctions, new PlaceFurnitureWrapper(player, location, id), event)); } + @Override public void handlePlayerBreakFurniture( Player player, Location location, @@ -2461,6 +2474,7 @@ public void handlePlayerBreakFurniture( .ifPresent(cFunctions -> handleFunctions(cFunctions, new BreakFurnitureWrapper(player, location, id), event)); } + @Override public void handlePlayerPlaceBlock(Player player, Block block, String blockID, Cancellable event) { if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) return; @@ -2475,6 +2489,7 @@ public void handlePlayerPlaceBlock(Player player, Block block, String blockID, C .ifPresent(cFunctions -> handleFunctions(cFunctions, new PlaceBlockWrapper(player, block, blockID), event)); } + @Override public void handleEntityTramplingBlock(Entity entity, Block block, Cancellable event) { if (entity instanceof Player player) { handlePlayerBreakBlock(player, block, "FARMLAND", event); @@ -2542,6 +2557,7 @@ public void handleEntityTramplingBlock(Entity entity, Block block, Cancellable e } } + @Override public void handleExplosion(Entity entity, List blocks, Cancellable event) { List locationsToRemove = new ArrayList<>(); List locationsToRemoveBlock = new ArrayList<>(); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java index 1d896d62f..bba6daac5 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java @@ -19,7 +19,7 @@ import io.lumine.mythiccrucible.events.MythicFurniturePlaceEvent; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; import org.bukkit.event.EventHandler; public class CrucibleListener extends AbstractCustomListener { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java index aa9a73ff2..523b01253 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java @@ -27,7 +27,7 @@ import io.lumine.mythiccrucible.items.furniture.Furniture; import io.lumine.mythiccrucible.items.furniture.FurnitureManager; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.item.CustomProvider; +import net.momirealms.customcrops.api.mechanic.item.CustomProvider; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java index 58ffe55e1..696314960 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java @@ -20,7 +20,7 @@ import dev.lone.itemsadder.api.CustomFurniture; import dev.lone.itemsadder.api.Events.*; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java index 62e093949..96cfdd0c6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java @@ -21,7 +21,7 @@ import dev.lone.itemsadder.api.CustomFurniture; import dev.lone.itemsadder.api.CustomStack; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.item.CustomProvider; +import net.momirealms.customcrops.api.mechanic.item.CustomProvider; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java index 41bc62e15..225f5bf07 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java @@ -17,16 +17,16 @@ package net.momirealms.customcrops.mechanic.item.custom.oraxen; +import io.th0rgal.oraxen.api.events.custom_block.noteblock.OraxenNoteBlockBreakEvent; +import io.th0rgal.oraxen.api.events.custom_block.noteblock.OraxenNoteBlockPlaceEvent; +import io.th0rgal.oraxen.api.events.custom_block.stringblock.OraxenStringBlockBreakEvent; +import io.th0rgal.oraxen.api.events.custom_block.stringblock.OraxenStringBlockPlaceEvent; import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; -import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockBreakEvent; -import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockPlaceEvent; -import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockBreakEvent; -import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockPlaceEvent; import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; import org.bukkit.event.EventHandler; public class OraxenListener extends AbstractCustomListener { @@ -98,10 +98,10 @@ public void onBreakFurniture(OraxenFurnitureBreakEvent event) { @EventHandler (ignoreCancelled = true) public void onInteractFurniture(OraxenFurnitureInteractEvent event) { super.onInteractFurniture( - event.getPlayer(), - LocationUtils.toBlockLocation(event.getBaseEntity().getLocation()), - event.getMechanic().getItemID(), - event.getBaseEntity(), + event.player(), + LocationUtils.toBlockLocation(event.baseEntity().getLocation()), + event.mechanic().getItemID(), + event.baseEntity(), event ); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java index 34f2327c5..bf2a37a86 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java @@ -24,7 +24,7 @@ import io.th0rgal.oraxen.mechanics.Mechanic; import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.item.CustomProvider; +import net.momirealms.customcrops.api.mechanic.item.CustomProvider; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Rotation; @@ -66,7 +66,7 @@ public void removeFurniture(Entity entity) { @Override public String getBlockID(Block block) { - Mechanic mechanic = OraxenBlocks.getOraxenBlock(block.getLocation()); + Mechanic mechanic = OraxenBlocks.getCustomBlockMechanic(block.getLocation()); if (mechanic == null) { return block.getType().name(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index b6898bfb0..02da2d0b5 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -36,7 +36,7 @@ import net.momirealms.customcrops.api.scheduler.CancellableTask; import net.momirealms.customcrops.api.scheduler.Scheduler; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.util.EventUtils; +import net.momirealms.customcrops.api.util.EventUtils; import org.bukkit.Bukkit; import org.bukkit.World; import org.jetbrains.annotations.NotNull; diff --git a/settings.gradle b/settings.gradle index a492555fb..0319b108e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,4 +2,5 @@ rootProject.name = 'CustomCrops' include(":plugin") include(":api") include(":legacy-api") +include 'oraxen-legacy' From 856efc01b74cbda273e2c54b3a1558dd7b8f892b Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 29 Apr 2024 14:37:39 +0800 Subject: [PATCH 032/329] [Small tweaks] Moved packages --- .../customcrops/api/manager/ItemManager.java | 65 +++++++++++-------- .../{ => custom}/AbstractCustomListener.java | 3 +- .../item/{ => custom}/CustomProvider.java | 2 +- .../oraxenlegacy/LegacyOraxenListener.java | 2 +- .../oraxenlegacy/LegacyOraxenProvider.java | 2 +- .../mechanic/item/ItemManagerImpl.java | 3 +- .../custom/crucible/CrucibleListener.java | 2 +- .../custom/crucible/CrucibleProvider.java | 2 +- .../custom/itemsadder/ItemsAdderListener.java | 2 +- .../custom/itemsadder/ItemsAdderProvider.java | 2 +- .../item/custom/oraxen/OraxenListener.java | 2 +- .../item/custom/oraxen/OraxenProvider.java | 2 +- settings.gradle | 2 +- 13 files changed, 53 insertions(+), 38 deletions(-) rename api/src/main/java/net/momirealms/customcrops/api/mechanic/item/{ => custom}/AbstractCustomListener.java (99%) rename api/src/main/java/net/momirealms/customcrops/api/mechanic/item/{ => custom}/CustomProvider.java (98%) diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java index 9ba33b6ed..4056afe8e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java @@ -328,6 +328,29 @@ public interface ItemManager extends Reloadable { @Nullable Crop.Stage getCropStageByStageID(String id); + /** + * Update a pot's block state + * + * @param location location + * @param pot pot config + * @param hasWater has water or not + * @param fertilizer fertilizer + */ + void updatePotState(Location location, Pot pot, boolean hasWater, Fertilizer fertilizer); + + /** + * Get the pots that can be watered with a watering can + * + * @param baseLocation the clicked pot's location + * @param width width of the working range + * @param length length of the working range + * @param yaw player's yaw + * @param potID pot's ID + * @return the pots that can be watered + */ + @NotNull + Collection getPotInRange(Location baseLocation, int width, int length, float yaw, String potID); + void handlePlayerInteractBlock( Player player, Block clickedBlock, @@ -369,32 +392,22 @@ void handlePlayerBreakFurniture( Cancellable event ); - void handlePlayerPlaceBlock(Player player, Block block, String blockID, Cancellable event); - - void handleEntityTramplingBlock(Entity entity, Block block, Cancellable event); - - void handleExplosion(Entity entity, List blocks, Cancellable event); + void handlePlayerPlaceBlock( + Player player, + Block block, + String blockID, + Cancellable event + ); - /** - * Update a pot's block state - * - * @param location location - * @param pot pot config - * @param hasWater has water or not - * @param fertilizer fertilizer - */ - void updatePotState(Location location, Pot pot, boolean hasWater, Fertilizer fertilizer); + void handleEntityTramplingBlock( + Entity entity, + Block block, + Cancellable event + ); - /** - * Get the pots that can be watered with a watering can - * - * @param baseLocation the clicked pot's location - * @param width width of the working range - * @param length length of the working range - * @param yaw player's yaw - * @param potID pot's ID - * @return the pots that can be watered - */ - @NotNull - Collection getPotInRange(Location baseLocation, int width, int length, float yaw, String potID); + void handleExplosion( + Entity entity, + List blocks, + Cancellable event + ); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java similarity index 99% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/item/AbstractCustomListener.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index f748704ab..fdb531b76 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.item; +package net.momirealms.customcrops.api.mechanic.item.custom; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.event.BoneMealDispenseEvent; @@ -23,6 +23,7 @@ import net.momirealms.customcrops.api.manager.ItemManager; import net.momirealms.customcrops.api.manager.VersionManager; import net.momirealms.customcrops.api.manager.WorldManager; +import net.momirealms.customcrops.api.mechanic.item.*; import net.momirealms.customcrops.api.mechanic.requirement.State; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/CustomProvider.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java similarity index 98% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/item/CustomProvider.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java index 20f99b253..95f258b8d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/CustomProvider.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.item; +package net.momirealms.customcrops.api.mechanic.item.custom; import net.momirealms.customcrops.api.manager.VersionManager; import net.momirealms.customcrops.api.mechanic.misc.CRotation; diff --git a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java index 58a027089..f9343a41d 100644 --- a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java +++ b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java @@ -25,7 +25,7 @@ import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockBreakEvent; import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockPlaceEvent; import net.momirealms.customcrops.api.manager.ItemManager; -import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import net.momirealms.customcrops.api.util.LocationUtils; import org.bukkit.event.EventHandler; diff --git a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java index 8d3be1a9b..b66c20de7 100644 --- a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java +++ b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java @@ -23,7 +23,7 @@ import io.th0rgal.oraxen.items.ItemBuilder; import io.th0rgal.oraxen.mechanics.Mechanic; import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; -import net.momirealms.customcrops.api.mechanic.item.CustomProvider; +import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Location; import org.bukkit.Material; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 0b47bb853..e6b72de4e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -29,6 +29,7 @@ import net.momirealms.customcrops.api.mechanic.condition.Conditions; import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; import net.momirealms.customcrops.api.mechanic.item.*; +import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; import net.momirealms.customcrops.api.mechanic.misc.CRotation; @@ -40,7 +41,7 @@ import net.momirealms.customcrops.api.mechanic.world.level.*; import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import net.momirealms.customcrops.mechanic.item.custom.crucible.CrucibleListener; import net.momirealms.customcrops.mechanic.item.custom.crucible.CrucibleProvider; import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderListener; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java index bba6daac5..9ffe1c40a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java @@ -19,7 +19,7 @@ import io.lumine.mythiccrucible.events.MythicFurniturePlaceEvent; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import org.bukkit.event.EventHandler; public class CrucibleListener extends AbstractCustomListener { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java index 523b01253..b2af7f8bc 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java @@ -27,7 +27,7 @@ import io.lumine.mythiccrucible.items.furniture.Furniture; import io.lumine.mythiccrucible.items.furniture.FurnitureManager; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.api.mechanic.item.CustomProvider; +import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java index 696314960..6d4509386 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java @@ -20,7 +20,7 @@ import dev.lone.itemsadder.api.CustomFurniture; import dev.lone.itemsadder.api.Events.*; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java index 96cfdd0c6..4e558c612 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java @@ -21,7 +21,7 @@ import dev.lone.itemsadder.api.CustomFurniture; import dev.lone.itemsadder.api.CustomStack; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.api.mechanic.item.CustomProvider; +import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java index 225f5bf07..52a62bf80 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java @@ -26,7 +26,7 @@ import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.api.mechanic.item.AbstractCustomListener; +import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import org.bukkit.event.EventHandler; public class OraxenListener extends AbstractCustomListener { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java index bf2a37a86..1ca15977e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java @@ -24,7 +24,7 @@ import io.th0rgal.oraxen.mechanics.Mechanic; import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.api.mechanic.item.CustomProvider; +import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Rotation; diff --git a/settings.gradle b/settings.gradle index 0319b108e..a6434087b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,5 +2,5 @@ rootProject.name = 'CustomCrops' include(":plugin") include(":api") include(":legacy-api") -include 'oraxen-legacy' +include("oraxen-legacy") From 8716b3022e80f25e221a2bbe215b5738bb7e046c Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sun, 5 May 2024 02:33:39 +0800 Subject: [PATCH 033/329] [Fix?] --- .../customcrops/api/CustomCropsPlugin.java | 1 - .../api/manager/ConfigManager.java | 35 ++- .../item/custom/AbstractCustomListener.java | 18 +- .../api/mechanic/world/SimpleLocation.java | 1 - .../world/level/CustomCropsChunk.java | 7 + .../world/level/CustomCropsWorld.java | 2 + .../mechanic/world/level/WorldInfoData.java | 1 - build.gradle.kts | 2 +- .../customcrops/CustomCropsPluginImpl.java | 2 +- .../manager/ConfigManagerImpl.java | 33 ++- .../mechanic/action/ActionManagerImpl.java | 2 +- .../condition/ConditionManagerImpl.java | 18 +- .../mechanic/item/ItemManagerImpl.java | 10 +- .../custom/crucible/CrucibleListener.java | 105 ++++---- .../custom/crucible/CrucibleProvider.java | 248 +++++++++--------- .../custom/itemsadder/ItemsAdderListener.java | 2 +- .../custom/itemsadder/ItemsAdderProvider.java | 2 +- .../item/custom/oraxen/OraxenListener.java | 2 +- .../item/custom/oraxen/OraxenProvider.java | 2 +- .../customcrops/mechanic/world/CChunk.java | 12 + .../customcrops/mechanic/world/CWorld.java | 10 +- .../mechanic/world/block/MemoryPot.java | 1 - plugin/src/main/resources/config.yml | 7 +- 23 files changed, 299 insertions(+), 224 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java index b772c497d..042c746d2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java +++ b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java @@ -18,7 +18,6 @@ package net.momirealms.customcrops.api; import net.momirealms.customcrops.api.manager.*; -import net.momirealms.customcrops.api.mechanic.world.season.Season; import net.momirealms.customcrops.api.scheduler.Scheduler; import org.bukkit.plugin.java.JavaPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java index 9d21173a2..7820679de 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.manager; import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; import org.bukkit.World; public abstract class ConfigManager implements Reloadable { @@ -120,21 +121,33 @@ public static boolean convertWorldOnLoad() { return instance.isConvertWorldOnLoad(); } - protected abstract boolean isConvertWorldOnLoad(); + public static boolean scarecrowProtectChunk() { + return instance.doesScarecrowProtectChunk(); + } + + public static ItemCarrier scarecrowItemCarrier() { + return instance.getScarecrowItemCarrier(); + } + + public static ItemCarrier glassItemCarrier() { + return instance.getGlassItemCarrier(); + } - protected abstract double[] getDefaultQualityRatio(); + public abstract boolean isConvertWorldOnLoad(); - protected abstract String getLang(); + public abstract double[] getDefaultQualityRatio(); - protected abstract boolean getDebugMode(); + public abstract String getLang(); - protected abstract boolean hasLegacyColorSupport(); + public abstract boolean getDebugMode(); - protected abstract int getMaximumPoolSize(); + public abstract boolean hasLegacyColorSupport(); - protected abstract int getKeepAliveTime(); + public abstract int getMaximumPoolSize(); - protected abstract int getCorePoolSize(); + public abstract int getKeepAliveTime(); + + public abstract int getCorePoolSize(); public abstract boolean isProtectLore(); @@ -162,5 +175,11 @@ public static boolean convertWorldOnLoad() { public abstract boolean isSyncSeasons(); + public abstract boolean doesScarecrowProtectChunk(); + + public abstract ItemCarrier getScarecrowItemCarrier(); + + public abstract ItemCarrier getGlassItemCarrier(); + public abstract World getReferenceWorld(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index fdb531b76..f39f7c4c1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -25,8 +25,11 @@ import net.momirealms.customcrops.api.manager.WorldManager; import net.momirealms.customcrops.api.mechanic.item.*; import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.mechanic.world.level.WorldGlass; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; import net.momirealms.customcrops.api.util.EventUtils; import org.bukkit.Location; import org.bukkit.Material; @@ -113,7 +116,7 @@ public void onInteractBlock(PlayerInteractEvent event) { ); } - @EventHandler (ignoreCancelled = false) + @EventHandler public void onInteractAir(PlayerInteractEvent event) { if (event.getHand() != EquipmentSlot.HAND) return; @@ -146,10 +149,15 @@ public void onBreakBlock(BlockBreakEvent event) { @EventHandler (ignoreCancelled = true) public void onPlaceBlock(BlockPlaceEvent event) { final Block block = event.getBlock(); - // prevent players from placing blocks on entities (crops/sprinklers) - if (CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(block.getLocation())).isPresent()) { - event.setCancelled(true); - return; + final Location location = block.getLocation(); + Optional customCropsBlock = CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(location)); + if (customCropsBlock.isPresent()) { + if (customCropsBlock.get() instanceof WorldPot || customCropsBlock.get() instanceof WorldGlass) { + CustomCropsPlugin.get().getWorldManager().removeAnythingAt(SimpleLocation.of(location)); + } else { + event.setCancelled(true); + return; + } } this.onPlaceBlock( event.getPlayer(), diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java index 0fc9ade72..6d48a7234 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java @@ -98,7 +98,6 @@ public int hashCode() { return hash; } - @Nullable public Location getBukkitLocation() { World world = Bukkit.getWorld(worldName); if (world == null) return null; diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java index 93b7457ef..97a4e9b95 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java @@ -308,6 +308,13 @@ public interface CustomCropsChunk { */ void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location); + /** + * If this chunk has scarecrow + * + * @return has or not + */ + boolean hasScarecrow(); + /** * Get CustomCrops sections * diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java index b22bce52b..25e1804d1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java @@ -344,6 +344,8 @@ public interface CustomCropsWorld { @Nullable CustomCropsBlock removeAnythingAt(SimpleLocation location); + boolean doesChunkHaveScarecrow(SimpleLocation location); + /** * If the amount of pot reaches the limitation * diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java index 15a828c18..51c97e198 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java @@ -18,7 +18,6 @@ package net.momirealms.customcrops.api.mechanic.world.level; import com.google.gson.annotations.SerializedName; -import net.momirealms.customcrops.api.manager.ConfigManager; import net.momirealms.customcrops.api.mechanic.world.season.Season; public class WorldInfoData { diff --git a/build.gradle.kts b/build.gradle.kts index c64a74921..d581e3639 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.7-BETA" + project.version = "3.4.7" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index 4498e00af..ab2438e2c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -24,6 +24,7 @@ import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; import net.momirealms.customcrops.api.manager.ConfigManager; import net.momirealms.customcrops.api.manager.CoolDownManager; +import net.momirealms.customcrops.api.util.EventUtils; import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.compatibility.IntegrationManagerImpl; import net.momirealms.customcrops.libraries.classpath.ReflectionClassPathAppender; @@ -38,7 +39,6 @@ import net.momirealms.customcrops.mechanic.requirement.RequirementManagerImpl; import net.momirealms.customcrops.mechanic.world.WorldManagerImpl; import net.momirealms.customcrops.scheduler.SchedulerImpl; -import net.momirealms.customcrops.api.util.EventUtils; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java index 88ee65a8b..af6e785e4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java @@ -25,6 +25,7 @@ import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.util.ConfigUtils; import org.bukkit.Bukkit; @@ -35,12 +36,13 @@ import java.io.File; import java.io.IOException; import java.lang.ref.WeakReference; +import java.util.Locale; import java.util.Objects; public class ConfigManagerImpl extends ConfigManager { - public static final String configVersion = "36"; - private CustomCropsPlugin plugin; + public static final String configVersion = "37"; + private final CustomCropsPlugin plugin; private String lang; private int maximumPoolSize; private int corePoolSize; @@ -63,6 +65,9 @@ public class ConfigManagerImpl extends ConfigManager { private boolean syncSeasons; private WeakReference referenceWorld; private boolean convertWorldOnLoad; + private boolean scarecrowProtectChunk; + private ItemCarrier scarecrowItemType; + private ItemCarrier glassItemType; public ConfigManagerImpl(CustomCropsPlugin plugin) { this.plugin = plugin; @@ -126,10 +131,13 @@ public void load() { greenhouse = mechanics.getBoolean("greenhouse.enable", true); greenhouseID = mechanics.getString("greenhouse.id"); greenhouseRange = mechanics.getInt("greenhouse.range", 5); + glassItemType = ItemCarrier.valueOf(mechanics.getString("greenhouse.type", "CHORUS").toUpperCase(Locale.ENGLISH)); scarecrow = mechanics.getBoolean("scarecrow.enable", true); scarecrowID = mechanics.getString("scarecrow.id"); scarecrowRange = mechanics.getInt("scarecrow.range", 7); + scarecrowProtectChunk = mechanics.getBoolean("scarecrow.protect-chunk", false); + scarecrowItemType = ItemCarrier.valueOf(mechanics.getString("scarecrow.type", "ITEM_FRAME").toUpperCase(Locale.ENGLISH)); syncSeasons = mechanics.getBoolean("sync-season.enable", true); if (syncSeasons) { @@ -163,17 +171,17 @@ public int getKeepAliveTime() { } @Override - protected boolean isConvertWorldOnLoad() { + public boolean isConvertWorldOnLoad() { return convertWorldOnLoad; } @Override - protected double[] getDefaultQualityRatio() { + public double[] getDefaultQualityRatio() { return defaultQualityRatio; } @Override - protected String getLang() { + public String getLang() { return lang; } @@ -247,6 +255,21 @@ public boolean isSyncSeasons() { return syncSeasons; } + @Override + public boolean doesScarecrowProtectChunk() { + return scarecrowProtectChunk; + } + + @Override + public ItemCarrier getScarecrowItemCarrier() { + return scarecrowItemType; + } + + @Override + public ItemCarrier getGlassItemCarrier() { + return glassItemType; + } + @Override public World getReferenceWorld() { return referenceWorld.get(); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index 6bdb473c2..0ac242c2a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -48,6 +48,7 @@ import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; import net.momirealms.customcrops.api.scheduler.CancellableTask; +import net.momirealms.customcrops.api.util.EventUtils; import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.compatibility.VaultHook; import net.momirealms.customcrops.manager.AdventureManagerImpl; @@ -58,7 +59,6 @@ import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; import net.momirealms.customcrops.util.ClassUtils; import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.customcrops.api.util.EventUtils; import net.momirealms.customcrops.util.ItemUtils; import org.bukkit.*; import org.bukkit.block.BlockFace; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java index 597c8c0a9..feb1f7483 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java @@ -168,18 +168,24 @@ private void registerCrowAttackCondition() { if (Math.random() > chance) return false; SimpleLocation location = block.getLocation(); if (ConfigManager.enableScarecrow()) { - int range = ConfigManager.scarecrowRange(); Optional world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName()); if (world.isEmpty()) return false; CustomCropsWorld customCropsWorld = world.get(); - for (int i = -range; i <= range; i++) { - for (int j = -range; j <= range; j++) { - for (int k : new int[]{0,-1,1}) { - if (customCropsWorld.getScarecrowAt(location.copy().add(i, k, j)).isPresent()) { - return false; + if (!ConfigManager.scarecrowProtectChunk()) { + int range = ConfigManager.scarecrowRange(); + for (int i = -range; i <= range; i++) { + for (int j = -range; j <= range; j++) { + for (int k : new int[]{0,-1,1}) { + if (customCropsWorld.getScarecrowAt(location.copy().add(i, k, j)).isPresent()) { + return false; + } } } } + } else { + if (customCropsWorld.doesChunkHaveScarecrow(location)) { + return false; + } } } if (!offline) diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index e6b72de4e..06372e6cf 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -29,6 +29,7 @@ import net.momirealms.customcrops.api.mechanic.condition.Conditions; import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; import net.momirealms.customcrops.api.mechanic.item.*; +import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; @@ -39,11 +40,9 @@ import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.*; +import net.momirealms.customcrops.api.util.EventUtils; import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; -import net.momirealms.customcrops.mechanic.item.custom.crucible.CrucibleListener; -import net.momirealms.customcrops.mechanic.item.custom.crucible.CrucibleProvider; import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderListener; import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderProvider; import net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenListener; @@ -61,7 +60,6 @@ import net.momirealms.customcrops.mechanic.item.impl.fertilizer.*; import net.momirealms.customcrops.mechanic.world.block.*; import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.customcrops.api.util.EventUtils; import net.momirealms.customcrops.util.ItemUtils; import net.momirealms.customcrops.util.RotationUtils; import org.bukkit.*; @@ -140,8 +138,8 @@ public ItemManagerImpl(CustomCropsPlugin plugin, AntiGriefLib antiGriefLib) { listener = new ItemsAdderListener(this); customProvider = new ItemsAdderProvider(); } else if (Bukkit.getPluginManager().getPlugin("MythicCrucible") != null) { - listener = new CrucibleListener(this); - customProvider = new CrucibleProvider(); +// listener = new CrucibleListener(this); +// customProvider = new CrucibleProvider(); } else { LogUtils.severe("======================================================"); LogUtils.severe(" Please install ItemsAdder or Oraxen as dependency."); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java index 9ffe1c40a..5f6020de9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java @@ -1,56 +1,49 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom.crucible; - -import io.lumine.mythiccrucible.events.MythicFurniturePlaceEvent; -import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; -import org.bukkit.event.EventHandler; - -public class CrucibleListener extends AbstractCustomListener { - - public CrucibleListener(ItemManagerImpl itemManager) { - super(itemManager); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCustomBlock() { - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceCustomBlock() { - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceFurniture(MythicFurniturePlaceEvent event) { - super.onPlaceFurniture( - event.getPlayer(), - event.getBlock().getLocation(), - event.getFurnitureItemContext().getItem().getInternalName(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakFurniture() { - } - - @EventHandler (ignoreCancelled = true) - public void onInteractFurniture() { - } -} +///* +// * Copyright (C) <2022> +// * +// * 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.mechanic.item.custom.crucible; +// +//import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; +//import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; +//import org.bukkit.event.EventHandler; +// +//public class CrucibleListener extends AbstractCustomListener { +// +// public CrucibleListener(ItemManagerImpl itemManager) { +// super(itemManager); +// } +// +// @EventHandler (ignoreCancelled = true) +// public void onBreakCustomBlock() { +// } +// +// @EventHandler (ignoreCancelled = true) +// public void onPlaceCustomBlock() { +// } +// +// @EventHandler (ignoreCancelled = true) +// public void onPlaceFurniture() { +// } +// +// @EventHandler (ignoreCancelled = true) +// public void onBreakFurniture() { +// } +// +// @EventHandler (ignoreCancelled = true) +// public void onInteractFurniture() { +// } +//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java index b2af7f8bc..af4d7db17 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java @@ -1,124 +1,124 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom.crucible; - -import io.lumine.mythic.bukkit.BukkitAdapter; -import io.lumine.mythic.bukkit.adapters.BukkitEntity; -import io.lumine.mythiccrucible.MythicCrucible; -import io.lumine.mythiccrucible.items.CrucibleItem; -import io.lumine.mythiccrucible.items.ItemManager; -import io.lumine.mythiccrucible.items.blocks.CustomBlockItemContext; -import io.lumine.mythiccrucible.items.blocks.CustomBlockManager; -import io.lumine.mythiccrucible.items.furniture.Furniture; -import io.lumine.mythiccrucible.items.furniture.FurnitureManager; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Entity; -import org.bukkit.inventory.ItemStack; - -import java.util.Optional; - -public class CrucibleProvider implements CustomProvider { - - private final ItemManager itemManager; - private final CustomBlockManager blockManager; - private final FurnitureManager furnitureManager; - - public CrucibleProvider() { - this.itemManager = MythicCrucible.inst().getItemManager(); - this.blockManager = itemManager.getCustomBlockManager(); - this.furnitureManager = itemManager.getFurnitureManager(); - } - - @Override - public boolean removeBlock(Location location) { - Block block = location.getBlock(); - if (block.getType() == Material.AIR) { - return false; - } - Optional optional = blockManager.getBlockFromBlock(block); - if (optional.isPresent()) { - optional.get().remove(block, null, false); - } else { - block.setType(Material.AIR); - } - return true; - } - - @Override - public void placeCustomBlock(Location location, String id) { - Optional optionalCI = itemManager.getItem(id); - if (optionalCI.isPresent()) { - location.getBlock().setBlockData(optionalCI.get().getBlockData().getBlockData()); - } else { - LogUtils.warn("Custom block(" + id +") doesn't exist in Crucible configs. Please double check if that block exists."); - } - } - - @Override - public Entity placeFurniture(Location location, String id) { - Optional optionalCI = itemManager.getItem(id); - if (optionalCI.isPresent()) { - return optionalCI.get().getFurnitureData().placeFrame(location.getBlock(), BlockFace.UP, 0f, null); - } else { - LogUtils.warn("Furniture(" + id +") doesn't exist in Crucible configs. Please double check if that furniture exists."); - return null; - } - } - - @Override - public void removeFurniture(Entity entity) { - Optional optional = furnitureManager.getFurniture(entity.getUniqueId()); - optional.ifPresent(furniture -> furniture.getFurnitureData().remove(furniture, null, false, false)); - } - - @Override - public String getBlockID(Block block) { - Optional optionalCB = blockManager.getBlockFromBlock(block); - return optionalCB.map(customBlockItemContext -> customBlockItemContext.getCrucibleItem().getInternalName()).orElse(block.getType().name()); - } - - @Override - public String getItemID(ItemStack itemStack) { - return itemManager.getItem(itemStack).map(CrucibleItem::getInternalName).orElse(null); - } - - @Override - public ItemStack getItemStack(String id) { - Optional optionalCI = itemManager.getItem(id); - return optionalCI.map(crucibleItem -> BukkitAdapter.adapt(crucibleItem.getMythicItem().generateItemStack(1))).orElse(null); - } - - @Override - public String getEntityID(Entity entity) { - Optional optionalCI = furnitureManager.getItemFromEntity(entity); - if (optionalCI.isPresent()) { - return optionalCI.get().getInternalName(); - } - return entity.getType().name(); - } - - @Override - public boolean isFurniture(Entity entity) { - return furnitureManager.isFurniture(new BukkitEntity(entity)); - } -} +///* +// * Copyright (C) <2022> +// * +// * 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.mechanic.item.custom.crucible; +// +//import io.lumine.mythic.bukkit.BukkitAdapter; +//import io.lumine.mythic.bukkit.adapters.BukkitEntity; +//import io.lumine.mythiccrucible.MythicCrucible; +//import io.lumine.mythiccrucible.items.CrucibleItem; +//import io.lumine.mythiccrucible.items.ItemManager; +//import io.lumine.mythiccrucible.items.blocks.CustomBlockItemContext; +//import io.lumine.mythiccrucible.items.blocks.CustomBlockManager; +//import io.lumine.mythiccrucible.items.furniture.Furniture; +//import io.lumine.mythiccrucible.items.furniture.FurnitureManager; +//import net.momirealms.customcrops.api.util.LogUtils; +//import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; +//import org.bukkit.Location; +//import org.bukkit.Material; +//import org.bukkit.block.Block; +//import org.bukkit.block.BlockFace; +//import org.bukkit.entity.Entity; +//import org.bukkit.inventory.ItemStack; +// +//import java.util.Optional; +// +//public class CrucibleProvider implements CustomProvider { +// +// private final ItemManager itemManager; +// private final CustomBlockManager blockManager; +// private final FurnitureManager furnitureManager; +// +// public CrucibleProvider() { +// this.itemManager = MythicCrucible.inst().getItemManager(); +// this.blockManager = itemManager.getCustomBlockManager(); +// this.furnitureManager = itemManager.getFurnitureManager(); +// } +// +// @Override +// public boolean removeBlock(Location location) { +// Block block = location.getBlock(); +// if (block.getType() == Material.AIR) { +// return false; +// } +// Optional optional = blockManager.getBlockFromBlock(block); +// if (optional.isPresent()) { +// optional.get().remove(block, null, false); +// } else { +// block.setType(Material.AIR); +// } +// return true; +// } +// +// @Override +// public void placeCustomBlock(Location location, String id) { +// Optional optionalCI = itemManager.getItem(id); +// if (optionalCI.isPresent()) { +// location.getBlock().setBlockData(optionalCI.get().getBlockData().getBlockData()); +// } else { +// LogUtils.warn("Custom block(" + id +") doesn't exist in Crucible configs. Please double check if that block exists."); +// } +// } +// +// @Override +// public Entity placeFurniture(Location location, String id) { +// Optional optionalCI = itemManager.getItem(id); +// if (optionalCI.isPresent()) { +// return optionalCI.get().getFurnitureData().placeFrame(location.getBlock(), BlockFace.UP, 0f, null); +// } else { +// LogUtils.warn("Furniture(" + id +") doesn't exist in Crucible configs. Please double check if that furniture exists."); +// return null; +// } +// } +// +// @Override +// public void removeFurniture(Entity entity) { +// Optional optional = furnitureManager.getFurniture(entity.getUniqueId()); +// optional.ifPresent(furniture -> furniture.getFurnitureData().remove(furniture, null, false, false)); +// } +// +// @Override +// public String getBlockID(Block block) { +// Optional optionalCB = blockManager.getBlockFromBlock(block); +// return optionalCB.map(customBlockItemContext -> customBlockItemContext.getCrucibleItem().getInternalName()).orElse(block.getType().name()); +// } +// +// @Override +// public String getItemID(ItemStack itemStack) { +// return itemManager.getItem(itemStack).map(CrucibleItem::getInternalName).orElse(null); +// } +// +// @Override +// public ItemStack getItemStack(String id) { +// Optional optionalCI = itemManager.getItem(id); +// return optionalCI.map(crucibleItem -> BukkitAdapter.adapt(crucibleItem.getMythicItem().generateItemStack(1))).orElse(null); +// } +// +// @Override +// public String getEntityID(Entity entity) { +// Optional optionalCI = furnitureManager.getItemFromEntity(entity); +// if (optionalCI.isPresent()) { +// return optionalCI.get().getInternalName(); +// } +// return entity.getType().name(); +// } +// +// @Override +// public boolean isFurniture(Entity entity) { +// return furnitureManager.isFurniture(new BukkitEntity(entity)); +// } +//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java index 6d4509386..86040ff28 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java @@ -19,8 +19,8 @@ import dev.lone.itemsadder.api.CustomFurniture; import dev.lone.itemsadder.api.Events.*; -import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; +import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java index 4e558c612..91773389a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java @@ -20,8 +20,8 @@ import dev.lone.itemsadder.api.CustomBlock; import dev.lone.itemsadder.api.CustomFurniture; import dev.lone.itemsadder.api.CustomStack; -import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; +import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java index 52a62bf80..272475b93 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java @@ -24,9 +24,9 @@ import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; +import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import org.bukkit.event.EventHandler; public class OraxenListener extends AbstractCustomListener { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java index 1ca15977e..c0b52ece6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java @@ -23,8 +23,8 @@ import io.th0rgal.oraxen.items.ItemBuilder; import io.th0rgal.oraxen.mechanics.Mechanic; import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; -import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; +import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Rotation; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java index b74f1c3d1..c05bb4dce 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -553,6 +553,18 @@ public int getSprinklerAmount() { return amount; } + @Override + public boolean hasScarecrow() { + for (CustomCropsSection section : getSections()) { + for (CustomCropsBlock block : section.getBlocks()) { + if (block instanceof WorldScarecrow) { + return true; + } + } + } + return false; + } + public CSection[] getSectionsForSerialization() { ArrayList sections = new ArrayList<>(); for (Map.Entry entry : loadedSections.entrySet()) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java index 02da2d0b5..04dbc52fd 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -35,8 +35,8 @@ import net.momirealms.customcrops.api.mechanic.world.season.Season; import net.momirealms.customcrops.api.scheduler.CancellableTask; import net.momirealms.customcrops.api.scheduler.Scheduler; -import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Bukkit; import org.bukkit.World; import org.jetbrains.annotations.NotNull; @@ -525,7 +525,7 @@ private CustomCropsChunk createOrGetChunk(ChunkPos chunkPos) { if (chunk != null) { return chunk; } - // is a loaded chunk, but it doesn't have customcrops data + // is a loaded chunk, but it doesn't have CustomCrops data if (bukkitWorld.isChunkLoaded(chunkPos.x(), chunkPos.z())) { chunk = new CChunk(this, chunkPos); loadChunk(chunk); @@ -535,6 +535,12 @@ private CustomCropsChunk createOrGetChunk(ChunkPos chunkPos) { } } + @Override + public boolean doesChunkHaveScarecrow(SimpleLocation location) { + Optional chunk = getLoadedChunkAt(location.getChunkPos()); + return chunk.map(CustomCropsChunk::hasScarecrow).orElse(false); + } + @Override public boolean isPotReachLimit(SimpleLocation location) { Optional chunk = getLoadedChunkAt(location.getChunkPos()); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java index f7e35c777..0fdac86c9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java @@ -27,7 +27,6 @@ import net.momirealms.customcrops.api.mechanic.item.Pot; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Location; diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 011766314..5002e70b0 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,5 +1,5 @@ # Don't change -config-version: '36' +config-version: '37' # Debug debug: false @@ -102,12 +102,17 @@ mechanics: scarecrow: enable: true id: '{0}scarecrow' + type: ITEM_FRAME range: 7 + # If this option is enabled, the range above would not longer take effect + # This option would make the scarecrow protect all the crops in a chunk instead of crops in certain range + protect-chunk: false # Greenhouse glass prevents crops from withering from season changing greenhouse: enable: true id: '{0}greenhouse_glass' + type: CHORUS range: 5 # Sync seasons From 28cb26845b19f8a485e0662c6ed0b25ebad6b757 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sun, 5 May 2024 02:41:11 +0800 Subject: [PATCH 034/329] Update config.yml --- plugin/src/main/resources/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 5002e70b0..9460b5e34 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -67,22 +67,22 @@ worlds: # which allows crops to grow at almost the same speed mode: RANDOM_TICK tick-interval: 1 - # Limit the max amount of crops in one chunk - max-per-chunk: 256 + # Limit the max amount of crops in one chunk (-1 = no limit) + max-per-chunk: -1 # Settings for pots pot: # RANDOM_TICK / SCHEDULED_TICK mode: SCHEDULED_TICK tick-interval: 3 - # Limit the max amount of pots in one chunk + # Limit the max amount of pots in one chunk (-1 = no limit) max-per-chunk: -1 # Settings for sprinklers sprinkler: # RANDOM_TICK / SCHEDULED_TICK mode: SCHEDULED_TICK tick-interval: 2 - # Limit the max amount of sprinklers in one chunk - max-per-chunk: 64 + # Limit the max amount of sprinklers in one chunk (-1 = no limit) + max-per-chunk: -1 # You can override the default settings for worlds here _WORLDS_: world_nether: From e390976cb7e2217a0b243db0626f47de96772a21 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 7 May 2024 22:08:57 +0800 Subject: [PATCH 035/329] 3.4.8 --- api/build.gradle.kts | 13 ++++ build.gradle.kts | 14 ++--- legacy-api/build.gradle.kts | 13 ++++ oraxen-j21/.gitignore | 42 +++++++++++++ oraxen-j21/build.gradle.kts | 18 ++++++ .../item/custom/oraxen/OraxenListener.java | 4 +- .../item/custom/oraxen/OraxenProvider.java | 0 oraxen-legacy/build.gradle.kts | 15 ++++- plugin/build.gradle.kts | 19 +++++- .../customcrops/CustomCropsPluginImpl.java | 42 +------------ .../libraries/dependencies/Dependency.java | 8 +-- .../mechanic/item/ItemManagerImpl.java | 17 ++++-- .../momirealms/customcrops/util/NBTUtils.java | 61 +++++++++++++++++++ settings.gradle | 1 + 14 files changed, 204 insertions(+), 63 deletions(-) create mode 100644 oraxen-j21/.gitignore create mode 100644 oraxen-j21/build.gradle.kts rename {plugin => oraxen-j21}/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java (96%) rename {plugin => oraxen-j21}/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java (100%) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/util/NBTUtils.java diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 7aa76c7ad..4e9c7bd20 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,4 +1,17 @@ dependencies { compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") implementation("com.flowpowered:flow-nbt:2.0.2") +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index d581e3639..b90284c4b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.7" + project.version = "3.4.8" apply() apply(plugin = "java") @@ -58,13 +58,10 @@ subprojects { } } - tasks.withType { - options.encoding = "UTF-8" - options.release.set(17) - } - tasks.shadowJar { - destinationDirectory.set(file("$rootDir/target")) + if (arrayListOf("plugin", "api").contains(project.name)) { + destinationDirectory.set(file("$rootDir/target")) + } archiveClassifier.set("") archiveFileName.set("CustomCrops-" + project.name + "-" + project.version + ".jar") } @@ -81,5 +78,4 @@ subprojects { } } } -} - +} \ No newline at end of file diff --git a/legacy-api/build.gradle.kts b/legacy-api/build.gradle.kts index 5094bf026..9612fc8fb 100644 --- a/legacy-api/build.gradle.kts +++ b/legacy-api/build.gradle.kts @@ -1,3 +1,16 @@ dependencies { compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } \ No newline at end of file diff --git a/oraxen-j21/.gitignore b/oraxen-j21/.gitignore new file mode 100644 index 000000000..b63da4551 --- /dev/null +++ b/oraxen-j21/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/oraxen-j21/build.gradle.kts b/oraxen-j21/build.gradle.kts new file mode 100644 index 000000000..9c4c0912c --- /dev/null +++ b/oraxen-j21/build.gradle.kts @@ -0,0 +1,18 @@ +dependencies { + compileOnly(project(":api")) + compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") + compileOnly("io.th0rgal:oraxen:2.0-SNAPSHOT") +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java b/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java rename to oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java index 272475b93..57fc32d85 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java +++ b/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java @@ -24,14 +24,14 @@ import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; +import net.momirealms.customcrops.api.manager.ItemManager; import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; import net.momirealms.customcrops.api.util.LocationUtils; -import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; import org.bukkit.event.EventHandler; public class OraxenListener extends AbstractCustomListener { - public OraxenListener(ItemManagerImpl itemManager) { + public OraxenListener(ItemManager itemManager) { super(itemManager); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java b/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java similarity index 100% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java rename to oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java diff --git a/oraxen-legacy/build.gradle.kts b/oraxen-legacy/build.gradle.kts index a9bc7a9af..2506dc038 100644 --- a/oraxen-legacy/build.gradle.kts +++ b/oraxen-legacy/build.gradle.kts @@ -1,5 +1,18 @@ dependencies { compileOnly(project(":api")) - compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT") + compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") compileOnly("com.github.oraxen:oraxen:1.172.0") +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } \ No newline at end of file diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index baa80beb1..8b902e6d5 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -30,7 +30,6 @@ dependencies { // Items compileOnly("com.github.LoneDev6:api-itemsadder:3.6.2-beta-r3-b") - compileOnly("io.th0rgal:oraxen:2.0-SNAPSHOT") compileOnly("pers.neige.neigeitems:NeigeItems:1.16.24") compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT") compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT") @@ -49,15 +48,16 @@ dependencies { implementation(project(":api")) implementation(project(":oraxen-legacy")) + implementation(project(":oraxen-j21")) implementation(project(":legacy-api")) compileOnly("net.kyori:adventure-api:4.16.0") compileOnly("net.kyori:adventure-platform-bukkit:4.3.2") compileOnly("com.github.Xiao-MoMi:AntiGriefLib:0.11") - compileOnly("com.github.Xiao-MoMi:BiomeAPI:0.3") + compileOnly("com.github.Xiao-MoMi:BiomeAPI:0.6") compileOnly("net.kyori:adventure-text-minimessage:4.16.0") compileOnly("net.kyori:adventure-text-serializer-legacy:4.16.0") - compileOnly("de.tr7zw:item-nbt-api:2.12.3") + compileOnly("de.tr7zw:item-nbt-api:2.12.4") compileOnly("org.bstats:bstats-bukkit:3.0.2") implementation("com.flowpowered:flow-nbt:2.0.2") implementation("com.github.luben:zstd-jni:1.5.6-2") @@ -74,4 +74,17 @@ tasks { relocate ("net.momirealms.biomeapi", "net.momirealms.customcrops.libraries.biomeapi") relocate ("net.momirealms.antigrieflib", "net.momirealms.customcrops.libraries.antigrieflib") } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index ab2438e2c..8a6b24801 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -17,8 +17,6 @@ package net.momirealms.customcrops; -import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion; -import de.tr7zw.changeme.nbtapi.utils.VersionChecker; import net.momirealms.antigrieflib.AntiGriefLib; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; @@ -39,10 +37,10 @@ import net.momirealms.customcrops.mechanic.requirement.RequirementManagerImpl; import net.momirealms.customcrops.mechanic.world.WorldManagerImpl; import net.momirealms.customcrops.scheduler.SchedulerImpl; +import net.momirealms.customcrops.util.NBTUtils; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @@ -99,7 +97,7 @@ public void onEnable() { this.hologramManager = new HologramManager(this); this.commandManager.init(); this.integrationManager.init(); - this.disableNBTAPILogs(); + NBTUtils.disableNBTAPILogs(); Migration.tryUpdating(); this.reload(); if (ConfigManager.metrics()) new Metrics(this, 16593); @@ -142,42 +140,6 @@ public void reload() { EventUtils.fireAndForget(new CustomCropsReloadEvent(this)); } - /** - * Disable NBT API logs - */ - private void disableNBTAPILogs() { - MinecraftVersion.disableBStats(); - MinecraftVersion.disableUpdateCheck(); - VersionChecker.hideOk = true; - try { - Field field = MinecraftVersion.class.getDeclaredField("version"); - field.setAccessible(true); - MinecraftVersion minecraftVersion; - try { - minecraftVersion = MinecraftVersion.valueOf(getVersionManager().getServerVersion().replace("v", "MC")); - } catch (IllegalArgumentException ex) { - minecraftVersion = MinecraftVersion.UNKNOWN; - } - field.set(MinecraftVersion.class, minecraftVersion); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - boolean hasGsonSupport; - try { - Class.forName("com.google.gson.Gson"); - hasGsonSupport = true; - } catch (Exception ex) { - hasGsonSupport = false; - } - try { - Field field= MinecraftVersion.class.getDeclaredField("hasGsonSupport"); - field.setAccessible(true); - field.set(Boolean.class, hasGsonSupport); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - @Override public void debug(String debug) { if (ConfigManager.debug()) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 96c0e3b9b..55edebe55 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -42,14 +42,14 @@ public enum Dependency { ASM( "org.ow2.asm", "asm", - "9.1", + "9.7", null, "asm" ), ASM_COMMONS( "org.ow2.asm", "asm-commons", - "9.1", + "9.7", null, "asm-commons" ), @@ -118,7 +118,7 @@ public enum Dependency { NBT_API( "de{}tr7zw", "item-nbt-api", - "2.12.3", + "2.12.4", "codemc", "item-nbt-api", Relocation.of("changeme", "de{}tr7zw{}changeme") @@ -134,7 +134,7 @@ public enum Dependency { BIOME_API( "com{}github{}Xiao-MoMi", "BiomeAPI", - "0.3", + "0.6", "jitpack", "biome-api", Relocation.of("biomeapi", "net{}momirealms{}biomeapi") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 06372e6cf..7f7c3171c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -45,8 +45,6 @@ import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderListener; import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderProvider; -import net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenListener; -import net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenProvider; import net.momirealms.customcrops.mechanic.item.custom.oraxenlegacy.LegacyOraxenListener; import net.momirealms.customcrops.mechanic.item.custom.oraxenlegacy.LegacyOraxenProvider; import net.momirealms.customcrops.mechanic.item.function.CFunction; @@ -81,6 +79,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; +import java.lang.reflect.Constructor; import java.util.*; public class ItemManagerImpl implements ItemManager { @@ -128,8 +127,18 @@ public ItemManagerImpl(CustomCropsPlugin plugin, AntiGriefLib antiGriefLib) { this.deadCrops = new HashSet<>(); if (Bukkit.getPluginManager().getPlugin("Oraxen") != null) { if (Bukkit.getPluginManager().getPlugin("Oraxen").getDescription().getVersion().startsWith("2")) { - listener = new OraxenListener(this); - customProvider = new OraxenProvider(); + try { + Class oraxenListenerClass = Class.forName("net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenListener"); + Constructor oraxenListenerConstructor = oraxenListenerClass.getDeclaredConstructor(ItemManager.class); + oraxenListenerConstructor.setAccessible(true); + this.listener = (AbstractCustomListener) oraxenListenerConstructor.newInstance(this); + Class oraxenProviderClass = Class.forName("net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenProvider"); + Constructor oraxenProviderConstructor = oraxenProviderClass.getDeclaredConstructor(ItemManager.class); + oraxenProviderConstructor.setAccessible(true); + this.customProvider = (CustomProvider) oraxenProviderConstructor.newInstance(); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } } else { listener = new LegacyOraxenListener(this); customProvider = new LegacyOraxenProvider(); diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/NBTUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/NBTUtils.java new file mode 100644 index 000000000..7a726bbe8 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/util/NBTUtils.java @@ -0,0 +1,61 @@ +package net.momirealms.customcrops.util; + +import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion; +import de.tr7zw.changeme.nbtapi.utils.VersionChecker; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import org.bukkit.Bukkit; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +public class NBTUtils { + + private NBTUtils() {} + + public static void disableNBTAPILogs() { + MinecraftVersion.disableBStats(); + MinecraftVersion.disableUpdateCheck(); + VersionChecker.hideOk = true; + try { + Field field = MinecraftVersion.class.getDeclaredField("version"); + field.setAccessible(true); + MinecraftVersion minecraftVersion; + try { + minecraftVersion = MinecraftVersion.valueOf(CustomCropsPlugin.get().getVersionManager().getServerVersion().replace("v", "MC")); + } catch (Exception ex) { + minecraftVersion = VERSION_TO_REVISION.getOrDefault(Bukkit.getServer().getBukkitVersion().split("-")[0], + MinecraftVersion.UNKNOWN); + } + field.set(MinecraftVersion.class, minecraftVersion); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + boolean hasGsonSupport; + try { + Class.forName("com.google.gson.Gson"); + hasGsonSupport = true; + } catch (Exception ex) { + hasGsonSupport = false; + } + try { + Field field= MinecraftVersion.class.getDeclaredField("hasGsonSupport"); + field.setAccessible(true); + field.set(Boolean.class, hasGsonSupport); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static final Map VERSION_TO_REVISION = new HashMap<>() { + { + this.put("1.20", MinecraftVersion.MC1_20_R1); + this.put("1.20.1", MinecraftVersion.MC1_20_R1); + this.put("1.20.2", MinecraftVersion.MC1_20_R2); + this.put("1.20.3", MinecraftVersion.MC1_20_R3); + this.put("1.20.4", MinecraftVersion.MC1_20_R3); + this.put("1.20.5", MinecraftVersion.MC1_20_R4); + this.put("1.20.6", MinecraftVersion.MC1_20_R4); + } + }; +} diff --git a/settings.gradle b/settings.gradle index a6434087b..5a9da6429 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,4 +3,5 @@ include(":plugin") include(":api") include(":legacy-api") include("oraxen-legacy") +include(":oraxen-j21") From b2949ce99fc5ea1009505828e53e0ead65932f6f Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 7 May 2024 22:26:06 +0800 Subject: [PATCH 036/329] Added some logs --- .../libraries/dependencies/Dependency.java | 1 - .../dependencies/DependencyManagerImpl.java | 19 ++++++++++--------- .../dependencies/DependencyRepository.java | 4 ++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 55edebe55..0af56be9d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -198,7 +198,6 @@ public String getFileName(String classifier) { String extra = classifier == null || classifier.isEmpty() ? "" : "-" + classifier; - return name + "-" + this.version + extra + ".jar"; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java index e24f51a1f..28f2c8c1a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java @@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableSet; import net.momirealms.customcrops.CustomCropsPluginImpl; +import net.momirealms.customcrops.api.util.LogUtils; import net.momirealms.customcrops.libraries.classpath.ClassPathAppender; import net.momirealms.customcrops.libraries.dependencies.classloader.IsolatedClassLoader; import net.momirealms.customcrops.libraries.dependencies.relocation.Relocation; @@ -64,13 +65,7 @@ public DependencyManagerImpl(CustomCropsPluginImpl plugin, ClassPathAppender cla this.registry = new DependencyRegistry(); this.cacheDirectory = setupCacheDirectory(plugin); this.classPathAppender = classPathAppender; - } - - private synchronized RelocationHandler getRelocationHandler() { - if (this.relocationHandler == null) { - this.relocationHandler = new RelocationHandler(this); - } - return this.relocationHandler; + this.relocationHandler = new RelocationHandler(this); } @Override @@ -155,7 +150,7 @@ private Path downloadDependency(Dependency dependency) throws DependencyDownload } DependencyDownloadException lastError = null; - + String fileName = dependency.getFileName(null); String forceRepo = dependency.getRepo(); if (forceRepo == null) { // attempt to download the dependency from each repo in order. @@ -164,7 +159,9 @@ private Path downloadDependency(Dependency dependency) throws DependencyDownload continue; } try { + LogUtils.info("Downloading dependency(" + fileName + ") from " + repo.getUrl() + dependency.getMavenRepoPath()); repo.download(dependency, file); + LogUtils.info("Successfully downloaded " + fileName); return file; } catch (DependencyDownloadException e) { lastError = e; @@ -174,7 +171,9 @@ private Path downloadDependency(Dependency dependency) throws DependencyDownload DependencyRepository repository = DependencyRepository.getByID(forceRepo); if (repository != null) { try { + LogUtils.info("Downloading dependency(" + fileName + ") from " + repository.getUrl() + dependency.getMavenRepoPath()); repository.download(dependency, file); + LogUtils.info("Successfully downloaded " + fileName); return file; } catch (DependencyDownloadException e) { lastError = e; @@ -197,7 +196,9 @@ private Path remapDependency(Dependency dependency, Path normalFile) throws Exce return remappedFile; } - getRelocationHandler().remap(normalFile, remappedFile, rules); + LogUtils.info("Remapping " + dependency.getFileName(null)); + relocationHandler.remap(normalFile, remappedFile, rules); + LogUtils.info("Successfully remapped " + dependency.getFileName(null)); return remappedFile; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java index 53b2e01a2..c97c28351 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java @@ -73,6 +73,10 @@ protected URLConnection openConnection(Dependency dependency) throws IOException this.id = id; } + public String getUrl() { + return url; + } + public static DependencyRepository getByID(String id) { for (DependencyRepository repository : values()) { if (id.equals(repository.id)) { From a3c1bae732a3c1e502f167bd777028ee4454fad1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 7 May 2024 22:27:21 +0800 Subject: [PATCH 037/329] Update jitpack.yml --- jitpack.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jitpack.yml b/jitpack.yml index 1e41e00b7..fbd76b779 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,2 +1,4 @@ -jdk: - - openjdk17 \ No newline at end of file +before_install: + - sdk install java 17.0.9-oracle + - sdk install java 21.0.2-oracle + - sdk use java 17.0.9-oracle \ No newline at end of file From 55ea394474c6346cede5e92a1628af44f0c76d1f Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 13 May 2024 03:57:28 +0800 Subject: [PATCH 038/329] Check point for future big changes --- .../item/custom/AbstractCustomListener.java | 13 ++++++ .../mechanic/item/custom/CustomProvider.java | 42 +++++++++---------- build.gradle.kts | 2 +- plugin/build.gradle.kts | 10 ++--- .../libraries/dependencies/Dependency.java | 2 +- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index f39f7c4c1..ae7871aca 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -36,6 +36,7 @@ import org.bukkit.block.Block; import org.bukkit.block.Dispenser; import org.bukkit.entity.Entity; +import org.bukkit.entity.FallingBlock; import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -100,6 +101,18 @@ public AbstractCustomListener(ItemManager itemManager) { } } + @EventHandler (ignoreCancelled = true) + public void onBlockFalling(EntityChangeBlockEvent event) { + if (event.getEntity() instanceof FallingBlock fallingBlock) { + final Block block = event.getBlock(); + final Location location = block.getLocation(); + Optional customCropsBlock = CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(location)); + if (customCropsBlock.isPresent()) { + fallingBlock.setCancelDrop(true); + } + } + } + @EventHandler (ignoreCancelled = true) public void onInteractBlock(PlayerInteractEvent event) { if (event.getHand() != EquipmentSlot.HAND) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java index 95f258b8d..be829eb85 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java @@ -69,29 +69,27 @@ default boolean isAir(Location location) { } default CRotation removeAnythingAt(Location location) { - if (!removeBlock(location)) { - Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toCenterLocation(location), 0.5,0.51,0.5); - entities.removeIf(entity -> { - EntityType type = entity.getType(); - return type != EntityType.ITEM_FRAME - && (!VersionManager.isHigherThan1_19_R3() || type != EntityType.ITEM_DISPLAY); - }); - if (entities.size() == 0) return CRotation.NONE; - CRotation previousCRotation; - Entity first = entities.stream().findFirst().get(); - if (first instanceof ItemFrame itemFrame) { - previousCRotation = CRotation.getByRotation(itemFrame.getRotation()); - } else if (VersionManager.isHigherThan1_19_R3()) { - previousCRotation = DisplayEntityUtils.getRotation(first); - } else { - previousCRotation = CRotation.NONE; - } - for (Entity entity : entities) { - removeFurniture(entity); - } - return previousCRotation; + removeBlock(location); + Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toCenterLocation(location), 0.5,0.51,0.5); + entities.removeIf(entity -> { + EntityType type = entity.getType(); + return type != EntityType.ITEM_FRAME + && (!VersionManager.isHigherThan1_19_R3() || type != EntityType.ITEM_DISPLAY); + }); + if (entities.isEmpty()) return CRotation.NONE; + CRotation previousCRotation; + Entity first = entities.stream().findFirst().get(); + if (first instanceof ItemFrame itemFrame) { + previousCRotation = CRotation.getByRotation(itemFrame.getRotation()); + } else if (VersionManager.isHigherThan1_19_R3()) { + previousCRotation = DisplayEntityUtils.getRotation(first); + } else { + previousCRotation = CRotation.NONE; } - return CRotation.NONE; + for (Entity entity : entities) { + removeFurniture(entity); + } + return previousCRotation; } default String getSomethingAt(Location location) { diff --git a/build.gradle.kts b/build.gradle.kts index b90284c4b..ea08a3eaf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.8" + project.version = "3.4.8.1" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 8b902e6d5..c5497717b 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -4,7 +4,7 @@ dependencies { compileOnly("com.infernalsuite.aswm:api:1.20.4-R0.1-SNAPSHOT") // Command - compileOnly("dev.jorel:commandapi-bukkit-core:9.3.0") + compileOnly("dev.jorel:commandapi-bukkit-core:9.4.1") // Common hooks compileOnly("me.clip:placeholderapi:2.11.5") @@ -12,7 +12,7 @@ dependencies { compileOnly("com.github.MilkBowl:VaultAPI:1.7") // Utils - compileOnly("dev.dejvokep:boosted-yaml:1.3.2") + compileOnly("dev.dejvokep:boosted-yaml:1.3.4") compileOnly("commons-io:commons-io:2.15.1") compileOnly("com.google.code.gson:gson:2.10.1") compileOnly("net.objecthunter:exp4j:0.4.8") @@ -50,13 +50,13 @@ dependencies { implementation(project(":oraxen-legacy")) implementation(project(":oraxen-j21")) implementation(project(":legacy-api")) - compileOnly("net.kyori:adventure-api:4.16.0") + compileOnly("net.kyori:adventure-api:4.17.0") compileOnly("net.kyori:adventure-platform-bukkit:4.3.2") compileOnly("com.github.Xiao-MoMi:AntiGriefLib:0.11") compileOnly("com.github.Xiao-MoMi:BiomeAPI:0.6") - compileOnly("net.kyori:adventure-text-minimessage:4.16.0") - compileOnly("net.kyori:adventure-text-serializer-legacy:4.16.0") + compileOnly("net.kyori:adventure-text-minimessage:4.17.0") + compileOnly("net.kyori:adventure-text-serializer-legacy:4.17.0") compileOnly("de.tr7zw:item-nbt-api:2.12.4") compileOnly("org.bstats:bstats-bukkit:3.0.2") implementation("com.flowpowered:flow-nbt:2.0.2") diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 0af56be9d..68cf961b9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -72,7 +72,7 @@ public enum Dependency { BOOSTED_YAML( "dev{}dejvokep", "boosted-yaml", - "1.3.2", + "1.3.4", null, "boosted-yaml", Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") From 30122cb0a92a916b402197582b26cd649d9896c2 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 13 May 2024 04:24:54 +0800 Subject: [PATCH 039/329] 3.4.9 --- .../item/custom/AbstractCustomListener.java | 3 +- .../mechanic/item/custom/CustomProvider.java | 4 +-- build.gradle.kts | 6 ++-- plugin/build.gradle.kts | 16 ++++----- .../customcrops/CustomCropsPluginImpl.java | 4 --- .../libraries/dependencies/Dependency.java | 34 ------------------- 6 files changed, 15 insertions(+), 52 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index ae7871aca..5c049d3d4 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -108,7 +108,8 @@ public void onBlockFalling(EntityChangeBlockEvent event) { final Location location = block.getLocation(); Optional customCropsBlock = CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(location)); if (customCropsBlock.isPresent()) { - fallingBlock.setCancelDrop(true); + event.setCancelled(true); + block.getWorld().dropItemNaturally(block.getLocation(), new ItemStack(fallingBlock.getBlockData().getMaterial())); } } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java index be829eb85..c6029ea16 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java @@ -65,7 +65,7 @@ default boolean isAir(Location location) { Location center = LocationUtils.toCenterLocation(location); Collection entities = center.getWorld().getNearbyEntities(center, 0.5,0.51,0.5); entities.removeIf(entity -> (entity instanceof Player || entity instanceof Item)); - return entities.size() == 0; + return entities.isEmpty(); } default CRotation removeAnythingAt(Location location) { @@ -115,7 +115,7 @@ default CRotation getRotation(Location location) { return type != EntityType.ITEM_FRAME && (!VersionManager.isHigherThan1_19_R3() || type != EntityType.ITEM_DISPLAY); }); - if (entities.size() == 0) return CRotation.NONE; + if (entities.isEmpty()) return CRotation.NONE; CRotation rotation; Entity first = entities.stream().findFirst().get(); if (first instanceof ItemFrame itemFrame) { diff --git a/build.gradle.kts b/build.gradle.kts index ea08a3eaf..91fa0de17 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,18 +2,18 @@ plugins { id("org.gradle.java") id("application") id("org.gradle.maven-publish") - id("com.github.johnrengelman.shadow") version "8.1.1" + id("io.github.goooler.shadow") version "8.1.7" } allprojects { project.group = "net.momirealms" - project.version = "3.4.8.1" + project.version = "3.4.9" apply() apply(plugin = "java") apply(plugin = "application") - apply(plugin = "com.github.johnrengelman.shadow") + apply(plugin = "io.github.goooler.shadow") apply(plugin = "org.gradle.maven-publish") application { diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index c5497717b..4ab591202 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -45,20 +45,20 @@ dependencies { compileOnly(files("libs/zaphkiel-2.0.24.jar")) compileOnly(files("libs/mcMMO-api.jar")) compileOnly(files("libs/RealisticSeasons-api.jar")) + compileOnly("org.bstats:bstats-bukkit:3.0.2") implementation(project(":api")) implementation(project(":oraxen-legacy")) implementation(project(":oraxen-j21")) implementation(project(":legacy-api")) - compileOnly("net.kyori:adventure-api:4.17.0") - compileOnly("net.kyori:adventure-platform-bukkit:4.3.2") - compileOnly("com.github.Xiao-MoMi:AntiGriefLib:0.11") - compileOnly("com.github.Xiao-MoMi:BiomeAPI:0.6") - compileOnly("net.kyori:adventure-text-minimessage:4.17.0") - compileOnly("net.kyori:adventure-text-serializer-legacy:4.17.0") - compileOnly("de.tr7zw:item-nbt-api:2.12.4") - compileOnly("org.bstats:bstats-bukkit:3.0.2") + implementation("net.kyori:adventure-api:4.17.0") + implementation("net.kyori:adventure-platform-bukkit:4.3.2") + implementation("net.kyori:adventure-text-minimessage:4.17.0") + implementation("net.kyori:adventure-text-serializer-legacy:4.17.0") + implementation("de.tr7zw:item-nbt-api:2.12.4") + implementation("com.github.Xiao-MoMi:AntiGriefLib:0.11") + implementation("com.github.Xiao-MoMi:BiomeAPI:0.6") implementation("com.flowpowered:flow-nbt:2.0.2") implementation("com.github.luben:zstd-jni:1.5.6-2") } diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index 8a6b24801..43b2f6d60 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -59,12 +59,8 @@ public void onLoad() { Dependency.GSON, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, - Dependency.ADVENTURE_API, Dependency.COMMAND_API, - Dependency.NBT_API, Dependency.BOOSTED_YAML, - Dependency.BIOME_API, - Dependency.ANTI_GRIEF, Dependency.BSTATS_BASE, Dependency.BSTATS_BUKKIT ) diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 68cf961b9..1fc5796c8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -77,16 +77,6 @@ public enum Dependency { "boosted-yaml", Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") ), - ADVENTURE_API( - "com.github.Xiao-MoMi", - "Adventure-Bundle", - "4.16.0", - "jitpack", - "adventure-bundle", - Relocation.of("adventure", "net{}kyori{}adventure"), - Relocation.of("option", "net{}kyori{}option"), - Relocation.of("examination", "net{}kyori{}examination") - ), H2_DRIVER( "com.h2database", "h2", @@ -115,30 +105,6 @@ public enum Dependency { null, "slf4j-api" ), - NBT_API( - "de{}tr7zw", - "item-nbt-api", - "2.12.4", - "codemc", - "item-nbt-api", - Relocation.of("changeme", "de{}tr7zw{}changeme") - ), - ANTI_GRIEF( - "com{}github{}Xiao-MoMi", - "AntiGriefLib", - "0.11", - "jitpack", - "antigrief-lib", - Relocation.of("antigrieflib", "net{}momirealms{}antigrieflib") - ), - BIOME_API( - "com{}github{}Xiao-MoMi", - "BiomeAPI", - "0.6", - "jitpack", - "biome-api", - Relocation.of("biomeapi", "net{}momirealms{}biomeapi") - ), BSTATS_BASE( "org{}bstats", "bstats-base", From 597c71f7895327f01c80f527887ef180e2759497 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Thu, 23 May 2024 16:41:36 +0800 Subject: [PATCH 040/329] Possibly fix entities on 1.18? --- .../world/level/CustomCropsChunk.java | 4 +++ build.gradle.kts | 2 +- .../customcrops/mechanic/world/CChunk.java | 16 ++++++++- .../mechanic/world/WorldManagerImpl.java | 33 ++++++++++++++++++- .../world/adaptor/BukkitWorldAdaptor.java | 7 ++-- .../world/adaptor/SlimeWorldAdaptor.java | 2 +- 6 files changed, 57 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java index 97a4e9b95..19acd36ca 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java @@ -331,10 +331,14 @@ public interface CustomCropsChunk { @Nullable CustomCropsSection getSection(int sectionID); + void resetUnloadedSeconds(); + /** * If the chunk can be pruned * * @return can be pruned or not */ boolean canPrune(); + + boolean isOfflineTaskNotified(); } diff --git a/build.gradle.kts b/build.gradle.kts index 91fa0de17..072d740b1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.9" + project.version = "3.4.10" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java index c05bb4dce..a3cf27717 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -51,6 +51,7 @@ public class CChunk implements CustomCropsChunk { private long lastLoadedTime; private int loadedSeconds; private int unloadedSeconds; + private boolean notified; public CChunk(CWorld cWorld, ChunkPos chunkPos) { this.cWorld = cWorld; @@ -60,6 +61,7 @@ public CChunk(CWorld cWorld, ChunkPos chunkPos) { this.unloadedSeconds = 0; this.tickedBlocks = Collections.synchronizedSet(new HashSet<>()); this.updateLastLoadedTime(); + this.notified = true; } public CChunk( @@ -88,6 +90,7 @@ public void updateLastLoadedTime() { @Override public void notifyOfflineUpdates() { + this.notified = true; long current = System.currentTimeMillis(); int offlineTimeInSeconds = (int) (current - this.lastLoadedTime) / 1000; CustomCropsPlugin.get().debug(chunkPos.toString() + " Offline seconds: " + offlineTimeInSeconds + "s."); @@ -595,9 +598,15 @@ public void setUnloadedSeconds(int unloadedSeconds) { this.unloadedSeconds = unloadedSeconds; } + @Override + public void resetUnloadedSeconds() { + this.unloadedSeconds = 0; + this.notified = false; + } + @Override public boolean canPrune() { - return loadedSections.size() == 0; + return loadedSections.isEmpty(); } public PriorityQueue getQueue() { @@ -607,4 +616,9 @@ public PriorityQueue getQueue() { public Set getTickedBlocks() { return tickedBlocks; } + + @Override + public boolean isOfflineTaskNotified() { + return notified; + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index 9de5ae474..4111ed7e8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -39,12 +39,16 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkEvent; import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.event.world.EntitiesLoadEvent; import org.jetbrains.annotations.NotNull; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; public class WorldManagerImpl implements WorldManager, Listener { @@ -517,9 +521,12 @@ public void handleChunkLoad(Chunk bukkitChunk) { } CustomCropsChunk chunk = optionalChunk.get(); + // load the entities if not loaded bukkitChunk.getEntities(); - chunk.notifyOfflineUpdates(); + if (bukkitChunk.isEntitiesLoaded()) { + chunk.notifyOfflineUpdates(); + } } @Override @@ -544,6 +551,30 @@ public void onChunkUnLoad(ChunkUnloadEvent event) { handleChunkUnload(event.getChunk()); } + @EventHandler + public void onEntitiesLoad(EntitiesLoadEvent event) { + + Chunk bukkitChunk = event.getChunk(); + Optional optional = getCustomCropsWorld(bukkitChunk.getWorld()); + if (optional.isEmpty()) + return; + + CustomCropsWorld customCropsWorld = optional.get(); + // offline grow part + if (!customCropsWorld.getWorldSetting().isOfflineTick()) return; + + ChunkPos chunkPos = ChunkPos.getByBukkitChunk(bukkitChunk); + + Optional optionalChunk = customCropsWorld.getLoadedChunkAt(chunkPos); + if (optionalChunk.isEmpty()) { + return; + } + + if (!optionalChunk.get().isOfflineTaskNotified()) { + optionalChunk.get().notifyOfflineUpdates(); + } + } + @Override public void saveChunkToCachedRegion(CustomCropsChunk chunk) { this.worldAdaptor.saveChunkToCachedRegion(chunk); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java index ab2957057..2f47476a3 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java @@ -48,6 +48,7 @@ import org.bukkit.World; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.event.world.WorldUnloadEvent; @@ -158,7 +159,7 @@ public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) CustomCropsChunk lazyChunk = cWorld.removeLazyChunkAt(chunkPos); if (lazyChunk != null) { CChunk cChunk = (CChunk) lazyChunk; - cChunk.setUnloadedSeconds(0); + cChunk.resetUnloadedSeconds(); cWorld.loadChunk(cChunk); long time2 = System.currentTimeMillis(); CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos + " from lazy chunks"); @@ -264,14 +265,14 @@ public void saveRegion(CustomCropsRegion customCropsRegion) { } } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) public void onWorldLoad(WorldLoadEvent event) { if (worldManager.isMechanicEnabled(event.getWorld())) { worldManager.loadWorld(event.getWorld()); } } - @EventHandler (ignoreCancelled = true) + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGH) public void onWorldUnload(WorldUnloadEvent event) { if (worldManager.isMechanicEnabled(event.getWorld())) worldManager.unloadWorld(event.getWorld()); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java index fd9390f44..f72ffc06c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java @@ -129,7 +129,7 @@ public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) CustomCropsChunk lazyChunk = customCropsWorld.removeLazyChunkAt(chunkPos); if (lazyChunk != null) { CChunk cChunk = (CChunk) lazyChunk; - cChunk.setUnloadedSeconds(0); + cChunk.resetUnloadedSeconds(); cWorld.loadChunk(cChunk); long time2 = System.currentTimeMillis(); CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos + " from lazy chunks"); From aeeac0fdb648100fb496571bc0a8cf98a219eda1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sun, 26 May 2024 22:35:11 +0800 Subject: [PATCH 041/329] Update CommandManager.java --- .../java/net/momirealms/customcrops/manager/CommandManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java index 807794436..e30cd5b64 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java @@ -56,7 +56,7 @@ public void init() { if (!CommandAPI.isLoaded()) CommandAPI.onLoad(new CommandAPIBukkitConfig(plugin).silentLogs(true)); new CommandAPICommand("customcrops") - .withPermission(CommandPermission.OP) + .withPermission("customcrops.admin") .withAliases("ccrops") .withSubcommands( getReloadCommand(), From 22cfc6768027e30b2f8ef29f18a39c3e34a2e058 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 8 Jun 2024 14:53:37 +0800 Subject: [PATCH 042/329] 3.4.11 --- build.gradle.kts | 2 +- plugin/build.gradle.kts | 1 + .../momirealms/customcrops/mechanic/item/ItemManagerImpl.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 072d740b1..800960598 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.10" + project.version = "3.4.11" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 4ab591202..c236caa5d 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -73,6 +73,7 @@ tasks { relocate ("dev.dejvokep.boostedyaml", "net.momirealms.customcrops.libraries.boostedyaml") relocate ("net.momirealms.biomeapi", "net.momirealms.customcrops.libraries.biomeapi") relocate ("net.momirealms.antigrieflib", "net.momirealms.customcrops.libraries.antigrieflib") + relocate ("net.objecthunter.exp4j", "net.momirealms.customcrops.libraries.exp4j") } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 7f7c3171c..87f3078ca 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -1810,7 +1810,7 @@ private void loadFertilizer(String key, ConfigurationSection section) { @SuppressWarnings("DuplicatedCode") private void loadCrop(String key, ConfigurationSection section) { ItemCarrier itemCarrier = ItemCarrier.valueOf(section.getString("type").toUpperCase(Locale.ENGLISH)); - if (itemCarrier != ItemCarrier.TRIPWIRE && itemCarrier != ItemCarrier.ITEM_DISPLAY && itemCarrier != ItemCarrier.ITEM_FRAME) { + if (itemCarrier != ItemCarrier.TRIPWIRE && itemCarrier != ItemCarrier.ITEM_DISPLAY && itemCarrier != ItemCarrier.ITEM_FRAME && itemCarrier != ItemCarrier.NOTE_BLOCK) { LogUtils.warn("Unsupported crop type: " + itemCarrier.name()); return; } From 92f4ad4c4d0ff6d7e3100d27ef2b34f0dc1246bb Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sat, 15 Jun 2024 03:11:23 +0800 Subject: [PATCH 043/329] 3.4.12 --- build.gradle.kts | 2 +- .../net/momirealms/customcrops/mechanic/misc/TempFakeItem.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 800960598..ebbca19ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.11" + project.version = "3.4.12" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java index fb74bbd9f..a6e06d197 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java @@ -43,7 +43,7 @@ public TempFakeItem(Location location, String item, int duration, Player viewer) SimpleLocation simpleLocation = SimpleLocation.of(location); ArrayList viewers = new ArrayList<>(); if (viewer == null) - for (Player player : Bukkit.getOnlinePlayers()) { + for (Player player : location.getWorld().getPlayers()) { if (simpleLocation.isNear(SimpleLocation.of(player.getLocation()), 48)) { viewers.add(player); } From f7f2b37c543af26e06eec8bc31f101cffa52af51 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Wed, 19 Jun 2024 03:06:39 +0800 Subject: [PATCH 044/329] 3.5.0 --- .../customcrops/api/CustomCropsPlugin.java | 2 + .../api/manager/VersionManager.java | 6 - build.gradle.kts | 2 +- plugin/build.gradle.kts | 11 +- .../customcrops/CustomCropsPluginImpl.java | 13 ++- .../compatibility/item/MMOItemsItemImpl.java | 4 +- .../item/MythicMobsItemImpl.java | 4 +- .../libraries/dependencies/Dependency.java | 11 +- .../manager/VersionManagerImpl.java | 70 +++--------- .../mechanic/action/ActionManagerImpl.java | 12 +- .../condition/ConditionManagerImpl.java | 6 +- .../mechanic/item/factory/AbstractItem.java | 104 ++++++++++++++++++ .../item/factory/BukkitItemFactory.java | 86 +++++++++++++++ .../mechanic/item/factory/ComponentKeys.java | 17 +++ .../mechanic/item/factory/Item.java | 39 +++++++ .../mechanic/item/factory/ItemFactory.java | 53 +++++++++ .../factory/impl/ComponentItemFactory.java | 99 +++++++++++++++++ .../factory/impl/UniversalItemFactory.java | 67 +++++++++++ .../mechanic/item/impl/WateringCanConfig.java | 40 ++++--- .../mechanic/misc/TempFakeItem.java | 1 - .../requirement/RequirementManagerImpl.java | 6 +- .../mechanic/world/WorldManagerImpl.java | 3 - .../customcrops/util/ItemUtils.java | 29 ++--- .../momirealms/customcrops/util/NBTUtils.java | 61 ---------- 24 files changed, 559 insertions(+), 187 deletions(-) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/AbstractItem.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/BukkitItemFactory.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ComponentKeys.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/Item.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ItemFactory.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/ComponentItemFactory.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/UniversalItemFactory.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/util/NBTUtils.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java index 042c746d2..78cf379c6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java +++ b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java @@ -122,4 +122,6 @@ public PlaceholderManager getPlaceholderManager() { public abstract void reload(); public abstract boolean doesHookedPluginExist(String plugin); + + public abstract String getServerVersion(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java index 7cbe24e75..3e874a66f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java @@ -69,12 +69,6 @@ public static String pluginVersion() { return instance.getPluginVersion(); } - public static String serverVersion() { - return instance.getServerVersion(); - } - - public abstract String getServerVersion(); - public static boolean spigot() { return instance.isSpigot(); } diff --git a/build.gradle.kts b/build.gradle.kts index ebbca19ea..e113982cc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.4.12" + project.version = "3.5.0" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index c236caa5d..3203d60f8 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -53,13 +53,15 @@ dependencies { implementation(project(":legacy-api")) implementation("net.kyori:adventure-api:4.17.0") - implementation("net.kyori:adventure-platform-bukkit:4.3.2") + implementation("net.kyori:adventure-platform-bukkit:4.3.3") implementation("net.kyori:adventure-text-minimessage:4.17.0") implementation("net.kyori:adventure-text-serializer-legacy:4.17.0") - implementation("de.tr7zw:item-nbt-api:2.12.4") implementation("com.github.Xiao-MoMi:AntiGriefLib:0.11") - implementation("com.github.Xiao-MoMi:BiomeAPI:0.6") + implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.16") implementation("com.flowpowered:flow-nbt:2.0.2") + implementation("com.saicone.rtag:rtag:1.5.4") + implementation("com.saicone.rtag:rtag-item:1.5.4") + compileOnly("de.tr7zw:item-nbt-api-plugin:2.13.0") implementation("com.github.luben:zstd-jni:1.5.6-2") } @@ -71,9 +73,10 @@ tasks { relocate ("org.objenesis", "net.momirealms.customcrops.libraries.objenesis") relocate ("org.bstats", "net.momirealms.customcrops.libraries.bstats") relocate ("dev.dejvokep.boostedyaml", "net.momirealms.customcrops.libraries.boostedyaml") - relocate ("net.momirealms.biomeapi", "net.momirealms.customcrops.libraries.biomeapi") + relocate ("net.momirealms.sparrow.heart", "net.momirealms.customcrops.libraries.sparrow") relocate ("net.momirealms.antigrieflib", "net.momirealms.customcrops.libraries.antigrieflib") relocate ("net.objecthunter.exp4j", "net.momirealms.customcrops.libraries.exp4j") + relocate ("com.saicone.rtag", "net.momirealms.customcrops.libraries.rtag") } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index 43b2f6d60..aff5ce672 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -33,11 +33,11 @@ import net.momirealms.customcrops.mechanic.action.ActionManagerImpl; import net.momirealms.customcrops.mechanic.condition.ConditionManagerImpl; import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; +import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; import net.momirealms.customcrops.mechanic.misc.migrator.Migration; import net.momirealms.customcrops.mechanic.requirement.RequirementManagerImpl; import net.momirealms.customcrops.mechanic.world.WorldManagerImpl; import net.momirealms.customcrops.scheduler.SchedulerImpl; -import net.momirealms.customcrops.util.NBTUtils; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; @@ -53,13 +53,14 @@ public class CustomCropsPluginImpl extends CustomCropsPlugin { @Override public void onLoad() { + this.versionManager = new VersionManagerImpl(this); this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader())); this.dependencyManager.loadDependencies(new ArrayList<>( List.of( Dependency.GSON, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, - Dependency.COMMAND_API, + versionManager.isMojmap() ? Dependency.COMMAND_API_MOJMAP : Dependency.COMMAND_API, Dependency.BOOSTED_YAML, Dependency.BSTATS_BASE, Dependency.BSTATS_BUKKIT @@ -70,7 +71,6 @@ public void onLoad() { @Override public void onEnable() { instance = this; - this.versionManager = new VersionManagerImpl(this); this.adventure = new AdventureManagerImpl(this); this.scheduler = new SchedulerImpl(this); this.configManager = new ConfigManagerImpl(this); @@ -93,7 +93,7 @@ public void onEnable() { this.hologramManager = new HologramManager(this); this.commandManager.init(); this.integrationManager.init(); - NBTUtils.disableNBTAPILogs(); + BukkitItemFactory.create(this); Migration.tryUpdating(); this.reload(); if (ConfigManager.metrics()) new Metrics(this, 16593); @@ -164,4 +164,9 @@ public boolean isHookedPluginEnabled(String plugin) { public boolean doesHookedPluginExist(String plugin) { return Bukkit.getPluginManager().getPlugin(plugin) != null; } + + @Override + public String getServerVersion() { + return Bukkit.getServer().getBukkitVersion().split("-")[0]; + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java index 4d9578f93..8336b1690 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.compatibility.item; -import de.tr7zw.changeme.nbtapi.NBTItem; +import io.lumine.mythic.lib.api.item.NBTItem; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem; @@ -44,7 +44,7 @@ public ItemStack buildItem(Player player, String id) { @Override public String getItemID(ItemStack itemStack) { - NBTItem nbtItem = new NBTItem(itemStack); + NBTItem nbtItem = NBTItem.get(itemStack); if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null; return nbtItem.getString("MMOITEMS_ITEM_TYPE") + ":" + nbtItem.getString("MMOITEMS_ITEM_ID"); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java index 392ee4c1c..1ff645ea4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java @@ -17,7 +17,6 @@ package net.momirealms.customcrops.compatibility.item; -import de.tr7zw.changeme.nbtapi.NBTItem; import io.lumine.mythic.bukkit.MythicBukkit; import net.momirealms.customcrops.api.integration.ItemLibrary; import org.bukkit.entity.Player; @@ -46,7 +45,6 @@ public ItemStack buildItem(Player player, String id) { @Override public String getItemID(ItemStack itemStack) { - NBTItem nbtItem = new NBTItem(itemStack); - return nbtItem.hasTag("MYTHIC_TYPE") ? nbtItem.getString("MYTHIC_TYPE") : null; + return mythicBukkit.getItemManager().getMythicTypeFromItem(itemStack); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 1fc5796c8..e1c20ffb8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -60,15 +60,22 @@ public enum Dependency { null, "jar-relocator" ), - COMMAND_API( "dev{}jorel", "commandapi-bukkit-shade", - "9.3.0", + "9.5.0", null, "commandapi-bukkit", Relocation.of("commandapi", "dev{}jorel{}commandapi") ), + COMMAND_API_MOJMAP( + "dev{}jorel", + "commandapi-bukkit-shade-mojang-mapped", + "9.5.0", + null, + "commandapi-bukkit-mojang-mapped", + Relocation.of("commandapi", "dev{}jorel{}commandapi") + ), BOOSTED_YAML( "dev{}dejvokep", "boosted-yaml", diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java index 15e231833..4840b605a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java @@ -32,55 +32,20 @@ public class VersionManagerImpl extends VersionManager { private final CustomCropsPlugin plugin; private final String pluginVersion; - private final String serverVersion; private boolean foliaScheduler; private final boolean isSpigot; - private final boolean isNewerThan1_19_R2; - private final boolean isNewerThan1_19_R3; - private final boolean isNewerThan1_20; - private final boolean isNewerThan1_20_R2; - private final boolean isNewerThan1_19; - private final boolean isNewerThan1_18; private boolean isMojmap; + private final float mcVersion; + @SuppressWarnings("deprecation") public VersionManagerImpl(CustomCropsPlugin plugin) { this.plugin = plugin; this.isSpigot = plugin.getServer().getName().equals("CraftBukkit"); this.pluginVersion = plugin.getDescription().getVersion(); - this.serverVersion = plugin.getServer().getClass().getPackage().getName().split("\\.")[3]; - String[] split = serverVersion.split("_"); - int main_ver = Integer.parseInt(split[1]); - - if (main_ver >= 20) { - isNewerThan1_20_R2 = Integer.parseInt(split[2].substring(1)) >= 2; - isNewerThan1_19_R2 = true; - isNewerThan1_19_R3 = true; - isNewerThan1_20 = true; - isNewerThan1_19 = true; - isNewerThan1_18 = true; - } else if (main_ver == 19) { - isNewerThan1_19_R2 = Integer.parseInt(split[2].substring(1)) >= 2; - isNewerThan1_19_R3 = Integer.parseInt(split[2].substring(1)) >= 3; - isNewerThan1_20 = false; - isNewerThan1_20_R2 = false; - isNewerThan1_19 = true; - isNewerThan1_18 = true; - } else if (main_ver == 18) { - isNewerThan1_19_R2 = false; - isNewerThan1_19_R3 = false; - isNewerThan1_20_R2 = false; - isNewerThan1_20 = false; - isNewerThan1_19 = false; - isNewerThan1_18 = true; - } else { - isNewerThan1_19_R2 = false; - isNewerThan1_19_R3 = false; - isNewerThan1_20_R2 = false; - isNewerThan1_20 = false; - isNewerThan1_19 = false; - isNewerThan1_18 = false; - } + + String[] split = plugin.getServerVersion().split("\\."); + this.mcVersion = Float.parseFloat(split[1] + "." + split[2]); try { Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); @@ -98,11 +63,6 @@ public VersionManagerImpl(CustomCropsPlugin plugin) { } } - @Override - public boolean isVersionNewerThan1_20_R2() { - return isNewerThan1_20_R2; - } - @Override public boolean hasRegionScheduler() { return foliaScheduler; @@ -113,11 +73,6 @@ public String getPluginVersion() { return pluginVersion; } - @Override - public String getServerVersion() { - return serverVersion; - } - @Override public boolean isSpigot() { return isSpigot; @@ -125,27 +80,32 @@ public boolean isSpigot() { @Override public boolean isVersionNewerThan1_19_R3() { - return isNewerThan1_19_R3; + return mcVersion >= 19.4; + } + + @Override + public boolean isVersionNewerThan1_20_R2() { + return mcVersion >= 20.2; } @Override public boolean isVersionNewerThan1_19() { - return isNewerThan1_19; + return mcVersion >= 19; } @Override public boolean isVersionNewerThan1_19_R2() { - return isNewerThan1_19_R2; + return mcVersion >= 19.3; } @Override public boolean isVersionNewerThan1_20() { - return isNewerThan1_20; + return mcVersion >= 20; } @Override public boolean isVersionNewerThan1_18() { - return isNewerThan1_18; + return mcVersion >= 18; } @Override diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index 0ac242c2a..5c253999c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -65,7 +65,9 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.ExperienceOrb; import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerItemDamageEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; @@ -827,10 +829,18 @@ private void registerItemDurabilityAction() { return state -> { if (Math.random() > chance) return; ItemStack itemStack = state.getItemInHand(); + if (amount > 0) { ItemUtils.increaseDurability(itemStack, amount); } else { - if (state.getPlayer().getGameMode() == GameMode.CREATIVE) return; + if (state.getPlayer().getGameMode() == GameMode.CREATIVE || itemStack.getItemMeta().isUnbreakable()) return; + + ItemMeta previousMeta = itemStack.getItemMeta().clone(); + PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(state.getPlayer(), itemStack, amount); + Bukkit.getPluginManager().callEvent(itemDamageEvent); + if (!itemStack.getItemMeta().equals(previousMeta) || itemDamageEvent.isCancelled()) { + return; + } ItemUtils.decreaseDurability(state.getPlayer(), itemStack, -amount); } }; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java index feb1f7483..851ce4aa2 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java @@ -17,7 +17,6 @@ package net.momirealms.customcrops.mechanic.condition; -import net.momirealms.biomeapi.BiomeAPI; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.common.Pair; import net.momirealms.customcrops.api.manager.ConditionManager; @@ -36,6 +35,7 @@ import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; import net.momirealms.customcrops.util.ClassUtils; import net.momirealms.customcrops.util.ConfigUtils; +import net.momirealms.sparrow.heart.SparrowHeart; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.data.type.Farmland; @@ -203,14 +203,14 @@ private void registerBiomeRequirement() { registerCondition("biome", (args) -> { HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); return (block, offline) -> { - String currentBiome = BiomeAPI.getBiomeAt(block.getLocation().getBukkitLocation()); + String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(block.getLocation().getBukkitLocation()); return biomes.contains(currentBiome); }; }); registerCondition("!biome", (args) -> { HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); return (block, offline) -> { - String currentBiome = BiomeAPI.getBiomeAt(block.getLocation().getBukkitLocation()); + String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(block.getLocation().getBukkitLocation()); return !biomes.contains(currentBiome); }; }); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/AbstractItem.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/AbstractItem.java new file mode 100644 index 000000000..4deb2c089 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/AbstractItem.java @@ -0,0 +1,104 @@ +package net.momirealms.customcrops.mechanic.item.factory; + +import net.momirealms.customcrops.api.CustomCropsPlugin; + +import java.util.List; +import java.util.Optional; + +public class AbstractItem implements Item { + + private final CustomCropsPlugin plugin; + private final ItemFactory factory; + private final R item; + + AbstractItem(CustomCropsPlugin plugin, ItemFactory factory, R item) { + this.plugin = plugin; + this.factory = factory; + this.item = item; + } + + @Override + public Item customModelData(Integer data) { + factory.customModelData(item, data); + return this; + } + + @Override + public Optional customModelData() { + return factory.customModelData(item); + } + + @Override + public Item damage(Integer data) { + factory.damage(item, data); + return this; + } + + @Override + public Optional damage() { + return factory.damage(item); + } + + @Override + public Item maxDamage(Integer data) { + factory.maxDamage(item, data); + return this; + } + + @Override + public Optional maxDamage() { + return factory.maxDamage(item); + } + + @Override + public Item lore(List lore) { + factory.lore(item, lore); + return this; + } + + @Override + public Optional> lore() { + return factory.lore(item); + } + + @Override + public Optional getTag(Object... path) { + return factory.getTag(item, path); + } + + @Override + public Item setTag(Object value, Object... path) { + factory.setTag(item, value, path); + return this; + } + + @Override + public boolean hasTag(Object... path) { + return factory.hasTag(item, path); + } + + @Override + public boolean removeTag(Object... path) { + return factory.removeTag(item, path); + } + + @Override + public I getItem() { + return factory.getItem(item); + } + + @Override + public I load() { + return factory.load(item); + } + + @Override + public I loadCopy() { + return factory.loadCopy(item); + } + + @Override + public void update() { + factory.update(item); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/BukkitItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/BukkitItemFactory.java new file mode 100644 index 000000000..efa092bdb --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/BukkitItemFactory.java @@ -0,0 +1,86 @@ +package net.momirealms.customcrops.mechanic.item.factory; + +import com.saicone.rtag.RtagItem; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.mechanic.item.factory.impl.ComponentItemFactory; +import net.momirealms.customcrops.mechanic.item.factory.impl.UniversalItemFactory; +import org.bukkit.inventory.ItemStack; + +import java.util.Objects; +import java.util.Optional; + +public abstract class BukkitItemFactory extends ItemFactory { + + private static BukkitItemFactory instance; + + protected BukkitItemFactory(CustomCropsPlugin plugin) { + super(plugin); + instance = this; + } + + public static BukkitItemFactory create(CustomCropsPlugin plugin) { + Objects.requireNonNull(plugin, "plugin"); + switch (plugin.getServerVersion()) { + case "1.17", "1.17.1", + "1.18", "1.18.1", "1.18.2", + "1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4", + "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4" -> { + return new UniversalItemFactory(plugin); + } + case "1.20.5", "1.20.6", + "1.21", "1.21.1", "1.21.2" -> { + return new ComponentItemFactory(plugin); + } + default -> throw new IllegalStateException("Unsupported server version: " + plugin.getServerVersion()); + } + } + + public static BukkitItemFactory getInstance() { + return instance; + } + + public Item wrap(ItemStack item) { + Objects.requireNonNull(item, "item"); + return wrap(new RtagItem(item)); + } + + @Override + protected void setTag(RtagItem item, Object value, Object... path) { + item.set(value, path); + } + + @Override + protected Optional getTag(RtagItem item, Object... path) { + return Optional.ofNullable(item.get(path)); + } + + @Override + protected boolean hasTag(RtagItem item, Object... path) { + return item.hasTag(path); + } + + @Override + protected boolean removeTag(RtagItem item, Object... path) { + return item.remove(path); + } + + @Override + protected void update(RtagItem item) { + item.update(); + } + + @Override + protected ItemStack load(RtagItem item) { + return item.load(); + } + + @Override + protected ItemStack getItem(RtagItem item) { + return item.getItem(); + } + + @Override + protected ItemStack loadCopy(RtagItem item) { + return item.loadCopy(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ComponentKeys.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ComponentKeys.java new file mode 100644 index 000000000..32eaad264 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ComponentKeys.java @@ -0,0 +1,17 @@ +package net.momirealms.customcrops.mechanic.item.factory; + +import net.kyori.adventure.key.Key; + +public class ComponentKeys { + + public static final String CUSTOM_MODEL_DATA = Key.key("minecraft", "custom_model_data").asString(); + public static final String MAX_DAMAGE = Key.key("minecraft", "max_damage").asString(); + public static final String CUSTOM_NAME = Key.key("minecraft", "custom_name").asString(); + public static final String LORE = Key.key("minecraft", "lore").asString(); + public static final String DAMAGE = Key.key("minecraft", "damage").asString(); + public static final String ENCHANTMENT_GLINT_OVERRIDE = Key.key("minecraft", "enchantment_glint_override").asString(); + public static final String HIDE_TOOLTIP = Key.key("minecraft", "hide_tooltip").asString(); + public static final String MAX_STACK_SIZE = Key.key("minecraft", "max_stack_size").asString(); + public static final String PROFILE = Key.key("minecraft", "profile").asString(); + public static final String UNBREAKABLE = Key.key("minecraft", "unbreakable").asString(); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/Item.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/Item.java new file mode 100644 index 000000000..35e2233bf --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/Item.java @@ -0,0 +1,39 @@ +package net.momirealms.customcrops.mechanic.item.factory; + +import java.util.List; +import java.util.Optional; + +public interface Item { + + Item customModelData(Integer data); + + Optional customModelData(); + + Item damage(Integer data); + + Optional damage(); + + Item maxDamage(Integer data); + + Optional maxDamage(); + + Item lore(List lore); + + Optional> lore(); + + Optional getTag(Object... path); + + Item setTag(Object value, Object... path); + + boolean hasTag(Object... path); + + boolean removeTag(Object... path); + + I getItem(); + + I load(); + + I loadCopy(); + + void update(); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ItemFactory.java new file mode 100644 index 000000000..72c62c313 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ItemFactory.java @@ -0,0 +1,53 @@ +package net.momirealms.customcrops.mechanic.item.factory; + +import net.momirealms.customcrops.api.CustomCropsPlugin; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public abstract class ItemFactory

{ + + protected final P plugin; + + protected ItemFactory(P plugin) { + this.plugin = plugin; + } + + public Item wrap(R item) { + Objects.requireNonNull(item, "item"); + return new AbstractItem<>(this.plugin, this, item); + } + + protected abstract Optional getTag(R item, Object... path); + + protected abstract void setTag(R item, Object value, Object... path); + + protected abstract boolean hasTag(R item, Object... path); + + protected abstract boolean removeTag(R item, Object... path); + + protected abstract void update(R item); + + protected abstract I load(R item); + + protected abstract I getItem(R item); + + protected abstract I loadCopy(R item); + + protected abstract Optional customModelData(R item); + + protected abstract void customModelData(R item, Integer data); + + protected abstract Optional> lore(R item); + + protected abstract void lore(R item, List lore); + + protected abstract Optional maxDamage(R item); + + protected abstract void maxDamage(R item, Integer data); + + protected abstract Optional damage(R item); + + protected abstract void damage(R item, Integer data); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/ComponentItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/ComponentItemFactory.java new file mode 100644 index 000000000..5266f84e5 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/ComponentItemFactory.java @@ -0,0 +1,99 @@ +package net.momirealms.customcrops.mechanic.item.factory.impl; + +import com.saicone.rtag.RtagItem; +import com.saicone.rtag.data.ComponentType; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; +import net.momirealms.customcrops.mechanic.item.factory.ComponentKeys; + +import java.util.List; +import java.util.Optional; + +@SuppressWarnings("UnstableApiUsage") +public class ComponentItemFactory extends BukkitItemFactory { + + public ComponentItemFactory(CustomCropsPlugin plugin) { + super(plugin); + } + + @Override + protected void customModelData(RtagItem item, Integer data) { + if (data == null) { + item.removeComponent(ComponentKeys.CUSTOM_MODEL_DATA); + } else { + item.setComponent(ComponentKeys.CUSTOM_MODEL_DATA, data); + } + } + + @Override + protected Optional customModelData(RtagItem item) { + if (!item.hasComponent(ComponentKeys.CUSTOM_MODEL_DATA)) return Optional.empty(); + return Optional.ofNullable( + (Integer) ComponentType.encodeJava( + ComponentKeys.CUSTOM_MODEL_DATA, + item.getComponent(ComponentKeys.CUSTOM_MODEL_DATA) + ).orElse(null) + ); + } + + @SuppressWarnings("unchecked") + @Override + protected Optional> lore(RtagItem item) { + if (item.getComponent(ComponentKeys.LORE) == null) return Optional.empty(); + return Optional.ofNullable( + (List) ComponentType.encodeJava( + ComponentKeys.LORE, + item.getComponent(ComponentKeys.LORE) + ).orElse(null) + ); + } + + @Override + protected void lore(RtagItem item, List lore) { + if (lore == null || lore.isEmpty()) { + item.removeComponent(ComponentKeys.LORE); + } else { + item.setComponent(ComponentKeys.LORE, lore); + } + } + + @Override + protected Optional maxDamage(RtagItem item) { + if (!item.hasComponent(ComponentKeys.MAX_DAMAGE)) return Optional.of((int) item.getItem().getType().getMaxDurability()); + return Optional.ofNullable( + (Integer) ComponentType.encodeJava( + ComponentKeys.MAX_DAMAGE, + item.getComponent(ComponentKeys.MAX_DAMAGE) + ).orElse(null) + ); + } + + @Override + protected void maxDamage(RtagItem item, Integer data) { + if (data == null) { + item.removeComponent(ComponentKeys.MAX_DAMAGE); + } else { + item.setComponent(ComponentKeys.MAX_DAMAGE, data); + } + } + + @Override + protected Optional damage(RtagItem item) { + if (!item.hasComponent(ComponentKeys.DAMAGE)) return Optional.of(0); + return Optional.ofNullable( + (Integer) ComponentType.encodeJava( + ComponentKeys.DAMAGE, + item.getComponent(ComponentKeys.DAMAGE) + ).orElse(null) + ); + } + + @Override + protected void damage(RtagItem item, Integer data) { + if (data == null) { + item.removeComponent(ComponentKeys.DAMAGE); + } else { + item.setComponent(ComponentKeys.DAMAGE, data); + } + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/UniversalItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/UniversalItemFactory.java new file mode 100644 index 000000000..5fb923c6c --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/UniversalItemFactory.java @@ -0,0 +1,67 @@ +package net.momirealms.customcrops.mechanic.item.factory.impl; + + + +import com.saicone.rtag.RtagItem; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; + +import java.util.List; +import java.util.Optional; + +public class UniversalItemFactory extends BukkitItemFactory { + + public UniversalItemFactory(CustomCropsPlugin plugin) { + super(plugin); + } + + @Override + protected void customModelData(RtagItem item, Integer data) { + if (data == null) { + item.remove("CustomModelData"); + } else { + item.set(data, "CustomModelData"); + } + } + + @Override + protected Optional customModelData(RtagItem item) { + if (!item.hasTag("CustomModelData")) return Optional.empty(); + return Optional.of(item.get("CustomModelData")); + } + + @Override + protected Optional> lore(RtagItem item) { + if (!item.hasTag("display", "Lore")) return Optional.empty(); + return Optional.of(item.get("display", "Lore")); + } + + @Override + protected void lore(RtagItem item, List lore) { + if (lore == null || lore.isEmpty()) { + item.remove("display", "Lore"); + } else { + item.set(lore, "display", "Lore"); + } + } + + @Override + protected Optional maxDamage(RtagItem item) { + return Optional.of((int) item.getItem().getType().getMaxDurability()); + } + + @Override + protected void maxDamage(RtagItem item, Integer data) { + throw new RuntimeException("Unsupported operation"); + } + + @Override + protected Optional damage(RtagItem item) { + return Optional.of(item.getOptional("Damage").or(0)); + } + + @Override + protected void damage(RtagItem item, Integer data) { + item.set(data, "Damage"); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java index fcd7b2a33..96caabf0e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java @@ -17,8 +17,7 @@ package net.momirealms.customcrops.mechanic.item.impl; -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTItem; +import com.saicone.rtag.item.ItemObject; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ScoreComponent; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; @@ -32,16 +31,15 @@ import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; import net.momirealms.customcrops.api.mechanic.requirement.Requirement; import net.momirealms.customcrops.mechanic.item.AbstractEventItem; +import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; +import net.momirealms.customcrops.mechanic.item.factory.Item; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.*; public class WateringCanConfig extends AbstractEventItem implements WateringCan { @@ -134,12 +132,19 @@ public PositiveFillMethod[] getPositiveFillMethods() { @Override public void updateItem(Player player, ItemStack itemStack, int water, Map args) { - int maxDurability = itemStack.getType().getMaxDurability(); - NBTItem nbtItem = new NBTItem(itemStack); + Item item = BukkitItemFactory.getInstance().wrap(itemStack); + int maxDurability = item.maxDamage().orElse((int) itemStack.getType().getMaxDurability()); + if (isInfinite()) water = storage; + item.setTag(water, "WaterAmount"); + if (maxDurability != 0) { + item.setTag((int) (maxDurability * (((double) storage - water) / storage)), "Damage"); + } + if (appearanceMap.containsKey(water)) { + item.customModelData(appearanceMap.get(water)); + } if (hasDynamicLore()) { - NBTCompound displayCompound = nbtItem.getOrCreateCompound("display"); - List lore = displayCompound.getStringList("Lore"); + List lore = new ArrayList<>(item.lore().orElse(List.of())); if (ConfigManager.protectLore()) { lore.removeIf(line -> { Component component = GsonComponentSerializer.gson().deserialize(line); @@ -157,24 +162,17 @@ public void updateItem(Player player, ItemStack itemStack, int water, Map item = BukkitItemFactory.getInstance().wrap(itemStack); + return (int) item.getTag("WaterAmount").orElse(0); } @Override diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java index a6e06d197..b5dbd417e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java @@ -22,7 +22,6 @@ import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.manager.PacketManager; import net.momirealms.customcrops.util.FakeEntityUtils; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java index 275616155..cb8d2893d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java @@ -17,7 +17,6 @@ package net.momirealms.customcrops.mechanic.requirement; -import net.momirealms.biomeapi.BiomeAPI; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.common.Pair; import net.momirealms.customcrops.api.integration.LevelInterface; @@ -39,6 +38,7 @@ import net.momirealms.customcrops.compatibility.papi.ParseUtils; import net.momirealms.customcrops.util.ClassUtils; import net.momirealms.customcrops.util.ConfigUtils; +import net.momirealms.sparrow.heart.SparrowHeart; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -368,7 +368,7 @@ private void registerBiomeRequirement() { registerRequirement("biome", (args, actions, advanced) -> { HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); return state -> { - String currentBiome = BiomeAPI.getBiomeAt(state.getLocation()); + String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(state.getLocation()); if (biomes.contains(currentBiome)) return true; if (advanced) triggerActions(actions, state); @@ -378,7 +378,7 @@ private void registerBiomeRequirement() { registerRequirement("!biome", (args, actions, advanced) -> { HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); return state -> { - String currentBiome = BiomeAPI.getBiomeAt(state.getLocation()); + String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(state.getLocation()); if (!biomes.contains(currentBiome)) return true; if (advanced) triggerActions(actions, state); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index 4111ed7e8..7a96567be 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -39,16 +39,13 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkEvent; import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.EntitiesLoadEvent; import org.jetbrains.annotations.NotNull; import java.util.*; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; public class WorldManagerImpl implements WorldManager, Listener { diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java index 175872ccb..89cfed459 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java @@ -17,7 +17,8 @@ package net.momirealms.customcrops.util; -import de.tr7zw.changeme.nbtapi.NBTItem; +import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; +import net.momirealms.customcrops.mechanic.item.factory.Item; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; @@ -84,20 +85,16 @@ public static void giveItem(Player player, ItemStack itemStack, int amount) { public static void increaseDurability(ItemStack itemStack, int amount) { if (itemStack == null || itemStack.getType() == Material.AIR) return; - NBTItem nbtItem = new NBTItem(itemStack); - if (nbtItem.getByte("Unbreakable") == 1) { - return; - } - int damage = Math.max(nbtItem.getInteger("Damage") - amount, 0); - nbtItem.setInteger("Damage", damage); - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); + Item item = BukkitItemFactory.getInstance().wrap(itemStack); + int damage = Math.max(item.damage().orElse(0) - amount, 0); + item.damage(damage); + itemStack.setItemMeta(item.load().getItemMeta()); } public static void decreaseDurability(Player player, ItemStack itemStack, int amount) { if (itemStack == null || itemStack.getType() == Material.AIR) return; - NBTItem nbtItem = new NBTItem(itemStack); - ItemMeta previousMeta = itemStack.getItemMeta().clone(); + ItemMeta previousMeta = itemStack.getItemMeta().clone(); PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(player, itemStack, amount, amount); Bukkit.getPluginManager().callEvent(itemDamageEvent); if (!itemStack.getItemMeta().equals(previousMeta) || itemDamageEvent.isCancelled()) { @@ -107,15 +104,13 @@ public static void decreaseDurability(Player player, ItemStack itemStack, int am if (Math.random() > (double) 1 / (unBreakingLevel + 1)) { return; } - if (nbtItem.getByte("Unbreakable") == 1) { - return; - } - int damage = nbtItem.getInteger("Damage") + amount; - if (damage > itemStack.getType().getMaxDurability()) { + Item item = BukkitItemFactory.getInstance().wrap(itemStack); + int damage = item.damage().orElse(0) + amount; + if (damage > item.maxDamage().orElse((int) itemStack.getType().getMaxDurability())) { itemStack.setAmount(0); } else { - nbtItem.setInteger("Damage", damage); - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); + item.damage(damage); + itemStack.setItemMeta(item.load().getItemMeta()); } } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/NBTUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/NBTUtils.java deleted file mode 100644 index 7a726bbe8..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/util/NBTUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.momirealms.customcrops.util; - -import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion; -import de.tr7zw.changeme.nbtapi.utils.VersionChecker; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import org.bukkit.Bukkit; - -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; - -public class NBTUtils { - - private NBTUtils() {} - - public static void disableNBTAPILogs() { - MinecraftVersion.disableBStats(); - MinecraftVersion.disableUpdateCheck(); - VersionChecker.hideOk = true; - try { - Field field = MinecraftVersion.class.getDeclaredField("version"); - field.setAccessible(true); - MinecraftVersion minecraftVersion; - try { - minecraftVersion = MinecraftVersion.valueOf(CustomCropsPlugin.get().getVersionManager().getServerVersion().replace("v", "MC")); - } catch (Exception ex) { - minecraftVersion = VERSION_TO_REVISION.getOrDefault(Bukkit.getServer().getBukkitVersion().split("-")[0], - MinecraftVersion.UNKNOWN); - } - field.set(MinecraftVersion.class, minecraftVersion); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - boolean hasGsonSupport; - try { - Class.forName("com.google.gson.Gson"); - hasGsonSupport = true; - } catch (Exception ex) { - hasGsonSupport = false; - } - try { - Field field= MinecraftVersion.class.getDeclaredField("hasGsonSupport"); - field.setAccessible(true); - field.set(Boolean.class, hasGsonSupport); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - private static final Map VERSION_TO_REVISION = new HashMap<>() { - { - this.put("1.20", MinecraftVersion.MC1_20_R1); - this.put("1.20.1", MinecraftVersion.MC1_20_R1); - this.put("1.20.2", MinecraftVersion.MC1_20_R2); - this.put("1.20.3", MinecraftVersion.MC1_20_R3); - this.put("1.20.4", MinecraftVersion.MC1_20_R3); - this.put("1.20.5", MinecraftVersion.MC1_20_R4); - this.put("1.20.6", MinecraftVersion.MC1_20_R4); - } - }; -} From 7e7ce6213866c7b2c8f628ddafc6593c704567f2 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Thu, 20 Jun 2024 00:15:19 +0800 Subject: [PATCH 045/329] 3.5.1 --- .../api/mechanic/item/custom/AbstractCustomListener.java | 4 ++-- .../customcrops/api/mechanic/world/SimpleLocation.java | 2 +- .../api/mechanic/world/level/AbstractCustomCropsBlock.java | 3 +++ build.gradle.kts | 2 +- .../libraries/dependencies/DependencyRepository.java | 4 ++-- .../customcrops/mechanic/action/ActionManagerImpl.java | 7 +++---- .../mechanic/world/adaptor/BukkitWorldAdaptor.java | 4 ++++ 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index 5c049d3d4..ce921bc14 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -195,7 +195,7 @@ public void onItemSpawn(ItemSpawnEvent event) { Sprinkler sprinkler = this.itemManager.getSprinklerBy3DItemID(itemID); if (sprinkler != null) { ItemStack newItem = this.itemManager.getItemStack(null, sprinkler.get2DItemID()); - if (newItem != null) { + if (newItem != null && newItem.getType() != Material.AIR) { newItem.setAmount(itemStack.getAmount()); item.setItemStack(newItem); } @@ -205,7 +205,7 @@ public void onItemSpawn(ItemSpawnEvent event) { Pot pot = this.itemManager.getPotByBlockID(itemID); if (pot != null) { ItemStack newItem = this.itemManager.getItemStack(null, pot.getDryItem()); - if (newItem != null) { + if (newItem != null && newItem.getType() != Material.AIR) { newItem.setAmount(itemStack.getAmount()); item.setItemStack(newItem); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java index 6d48a7234..14c623d15 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java @@ -55,7 +55,7 @@ public String getWorldName() { } public ChunkPos getChunkPos() { - return new ChunkPos(x >> 4, z >> 4); + return new ChunkPos((int) Math.floor((double) getX() / 16), (int) Math.floor((double) getZ() / 16)); } public SimpleLocation add(int x, int y, int z) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java index 56b0b7a41..1e9443397 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java @@ -83,6 +83,9 @@ public SimpleLocation getLocation() { * @return can be ticked or not */ public boolean canTick(int interval) { + if (interval <= 0) { + return false; + } if (interval == 1) { return true; } diff --git a/build.gradle.kts b/build.gradle.kts index e113982cc..fb389872b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.0" + project.version = "3.5.1" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java index c97c28351..31ed18fb4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java @@ -47,8 +47,8 @@ public enum DependencyRepository { @Override protected URLConnection openConnection(Dependency dependency) throws IOException { URLConnection connection = super.openConnection(dependency); - connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(5)); - connection.setReadTimeout((int) TimeUnit.SECONDS.toMillis(5)); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); return connection; } }, diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index 5c253999c..b330eba87 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -60,6 +60,8 @@ import net.momirealms.customcrops.util.ClassUtils; import net.momirealms.customcrops.util.ConfigUtils; import net.momirealms.customcrops.util.ItemUtils; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.argument.HandSlot; import org.bukkit.*; import org.bukkit.block.BlockFace; import org.bukkit.configuration.ConfigurationSection; @@ -434,10 +436,7 @@ private void registerSwingHandAction() { return state -> { if (Math.random() > chance) return; if (state.getPlayer() == null) return; - PacketContainer animationPacket = new PacketContainer(PacketType.Play.Server.ANIMATION); - animationPacket.getIntegers().write(0, state.getPlayer().getEntityId()); - animationPacket.getIntegers().write(1, arg ? 0 : 3); - PacketManager.getInstance().send(state.getPlayer(), animationPacket); + SparrowHeart.getInstance().swingHand(state.getPlayer(), arg ? HandSlot.MAIN : HandSlot.OFF); }; }); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java index 2f47476a3..5b4fd7d2c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java @@ -243,6 +243,10 @@ public void unloadChunkData(CustomCropsWorld ccWorld, ChunkPos chunkPos) { @Override public void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk) { CustomCropsRegion customCropsRegion = customCropsChunk.getCustomCropsRegion(); + if (customCropsRegion == null) { + LogUtils.severe(customCropsChunk.getChunkPos().toString() + " unloaded before chunk saving"); + return; + } SerializableChunk serializableChunk = toSerializableChunk((CChunk) customCropsChunk); if (serializableChunk.canPrune()) { customCropsRegion.removeChunk(customCropsChunk.getChunkPos()); From 701eb7bb9704e2bc77946739fbefeaefbcec4962 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Fri, 21 Jun 2024 01:34:20 +0800 Subject: [PATCH 046/329] 3.5.2 --- .../customcrops/api/mechanic/item/Pot.java | 2 +- build.gradle.kts | 2 +- plugin/build.gradle.kts | 4 +- plugin/libs/Sparrow-Heart-0.17.jar | Bin 0 -> 147369 bytes .../mechanic/item/ItemManagerImpl.java | 7 ++- .../mechanic/world/block/MemoryPot.java | 46 +++++++++++++++--- 6 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 plugin/libs/Sparrow-Heart-0.17.jar diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java index 973c5e0f3..2a1628b45 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java @@ -126,7 +126,7 @@ public interface Pot extends KeyItem { String getBlockState(boolean water, FertilizerType type); /** - * Is the pot a vanilla blocks + * Is the pot a vanilla block */ boolean isVanillaBlock(); diff --git a/build.gradle.kts b/build.gradle.kts index fb389872b..7fc9d36aa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.1" + project.version = "3.5.2" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 3203d60f8..125f0fe21 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -52,16 +52,16 @@ dependencies { implementation(project(":oraxen-j21")) implementation(project(":legacy-api")) + implementation(files("libs/Sparrow-Heart-0.17.jar")) implementation("net.kyori:adventure-api:4.17.0") implementation("net.kyori:adventure-platform-bukkit:4.3.3") implementation("net.kyori:adventure-text-minimessage:4.17.0") implementation("net.kyori:adventure-text-serializer-legacy:4.17.0") implementation("com.github.Xiao-MoMi:AntiGriefLib:0.11") - implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.16") +// implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.16") implementation("com.flowpowered:flow-nbt:2.0.2") implementation("com.saicone.rtag:rtag:1.5.4") implementation("com.saicone.rtag:rtag-item:1.5.4") - compileOnly("de.tr7zw:item-nbt-api-plugin:2.13.0") implementation("com.github.luben:zstd-jni:1.5.6-2") } diff --git a/plugin/libs/Sparrow-Heart-0.17.jar b/plugin/libs/Sparrow-Heart-0.17.jar new file mode 100644 index 0000000000000000000000000000000000000000..84965399f67431a93c7fae86e886ca45647c6426 GIT binary patch literal 147369 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

!wmIwL35g_ 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 functions, ConditionWrappe @Override public void updatePotState(Location location, Pot pot, boolean hasWater, Fertilizer fertilizer) { + Block block = location.getBlock(); + Pot currentPot = getPotByBlock(block); + if (currentPot != pot) { + plugin.getWorldManager().removePotAt(SimpleLocation.of(location)); + return; + } if (pot.isVanillaBlock()) { - Block block = location.getBlock(); if (block.getBlockData() instanceof Farmland farmland) { farmland.setMoisture(hasWater ? 7 : 0); block.setBlockData(farmland); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java index 0fdac86c9..1bc313061 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java @@ -217,23 +217,55 @@ private void tick() { } int water = getWater(); - if (water > 0) { - if (--water <= 0) { - loseWater = true; + + SimpleLocation location = getLocation(); + Location bukkitLocation = location.getBukkitLocation(); + if (bukkitLocation == null) return; + World world = bukkitLocation.getWorld(); + + outer: { + if (water > 0) { + if (pot.isRainDropAccepted()) { + if (world.hasStorm() || (!world.isClearWeather() && !world.isThundering())) { + double temperature = world.getTemperature(location.getX(), location.getY(), location.getZ()); + if (temperature > 0.15 && temperature < 0.85) { + Block highest = world.getHighestBlockAt(location.getX(), location.getZ()); + if (highest.getLocation().getY() == location.getY()) { + break outer; + } + } + } + } + + if (pot.isNearbyWaterAccepted()) { + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + for (int k : new int[]{0, 1}) { + Block block = bukkitLocation.clone().add(i,k,j).getBlock(); + if (block.getType() == Material.WATER || (block.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged())) { + break outer; + } + } + } + } + } + + if (--water <= 0) { + loseWater = true; + } + setWater(water); } - setWater(water); } if (loseFertilizer || loseWater) { - Location location = getLocation().getBukkitLocation(); CustomCropsPlugin.get().getScheduler().runTaskSync(() -> CustomCropsPlugin.get().getItemManager() .updatePotState( - location, + bukkitLocation, pot, getWater() > 0, getFertilizer() - ), location + ), bukkitLocation ); } } From 7089e118102d1a552d3a39967bac19a6f43d653e Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sun, 23 Jun 2024 22:17:28 +0800 Subject: [PATCH 047/329] 3.5.3 --- build.gradle.kts | 2 +- plugin/libs/Sparrow-Heart-0.17.jar | Bin 147369 -> 147367 bytes .../world/adaptor/BukkitWorldAdaptor.java | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7fc9d36aa..9ce2809b7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.2" + project.version = "3.5.3" apply() apply(plugin = "java") diff --git a/plugin/libs/Sparrow-Heart-0.17.jar b/plugin/libs/Sparrow-Heart-0.17.jar index 84965399f67431a93c7fae86e886ca45647c6426..dae4f422db277b374c2674ffb99bc56a9773fd51 100644 GIT binary patch delta 2609 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 delta 2712 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 Date: Thu, 27 Jun 2024 00:09:00 +0800 Subject: [PATCH 048/329] 1.21 --- build.gradle.kts | 2 +- .../customcrops/CustomCropsPluginImpl.java | 1 + .../libraries/dependencies/Dependency.java | 12 ++++++++++-- .../customcrops/manager/VersionManagerImpl.java | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9ce2809b7..8bafccd74 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.3" + project.version = "3.5.4" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java index aff5ce672..844e74735 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -58,6 +58,7 @@ public void onLoad() { this.dependencyManager.loadDependencies(new ArrayList<>( List.of( Dependency.GSON, + Dependency.EXP4J, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, versionManager.isMojmap() ? Dependency.COMMAND_API_MOJMAP : Dependency.COMMAND_API, diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index e1c20ffb8..6f76bf587 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -63,7 +63,7 @@ public enum Dependency { COMMAND_API( "dev{}jorel", "commandapi-bukkit-shade", - "9.5.0", + "9.5.1", null, "commandapi-bukkit", Relocation.of("commandapi", "dev{}jorel{}commandapi") @@ -71,7 +71,7 @@ public enum Dependency { COMMAND_API_MOJMAP( "dev{}jorel", "commandapi-bukkit-shade-mojang-mapped", - "9.5.0", + "9.5.1", null, "commandapi-bukkit-mojang-mapped", Relocation.of("commandapi", "dev{}jorel{}commandapi") @@ -134,6 +134,14 @@ public enum Dependency { "2.10.1", null, "gson" + ), + EXP4J( + "net{}objecthunter", + "exp4j", + "0.4.8", + null, + "exp4j", + Relocation.of("exp4j", "net{}objecthunter{}exp4j") ); private final String mavenRepoPath; diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java index 4840b605a..47b1b96a6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java @@ -45,7 +45,7 @@ public VersionManagerImpl(CustomCropsPlugin plugin) { this.pluginVersion = plugin.getDescription().getVersion(); String[] split = plugin.getServerVersion().split("\\."); - this.mcVersion = Float.parseFloat(split[1] + "." + split[2]); + this.mcVersion = Float.parseFloat(split[1] + "." + (split.length >= 3 ? split[2] : "0")); try { Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); From 154465b7fd7012d5648031e664566ca07858e951 Mon Sep 17 00:00:00 2001 From: yuyi2439 Date: Sun, 30 Jun 2024 16:51:16 +0800 Subject: [PATCH 049/329] change Zrips:Jobs tag --- plugin/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 125f0fe21..a6a61a665 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -25,7 +25,7 @@ dependencies { compileOnly("net.Indyuce:MMOCore-API:1.12-SNAPSHOT") compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.23") - compileOnly("com.github.Zrips:Jobs:4.17.2") + compileOnly("com.github.Zrips:Jobs:v4.17.2") compileOnly("dev.aurelium:auraskills-api-bukkit:2.0.0-SNAPSHOT") // Items From caef3c8ae541f6cce2ee31898b2db5fc48fc70ff Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 1 Jul 2024 00:27:52 +0800 Subject: [PATCH 050/329] 3.5.5 --- build.gradle.kts | 2 +- plugin/build.gradle.kts | 2 +- plugin/libs/Sparrow-Heart-0.17.jar | Bin 147367 -> 0 bytes plugin/libs/Sparrow-Heart-0.19.jar | Bin 0 -> 177287 bytes .../mechanic/action/ActionManagerImpl.java | 3 +-- 5 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 plugin/libs/Sparrow-Heart-0.17.jar create mode 100644 plugin/libs/Sparrow-Heart-0.19.jar diff --git a/build.gradle.kts b/build.gradle.kts index 8bafccd74..54d408291 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.4" + project.version = "3.5.5" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 125f0fe21..fd71b84b2 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -52,7 +52,7 @@ dependencies { implementation(project(":oraxen-j21")) implementation(project(":legacy-api")) - implementation(files("libs/Sparrow-Heart-0.17.jar")) + implementation(files("libs/Sparrow-Heart-0.19.jar")) implementation("net.kyori:adventure-api:4.17.0") implementation("net.kyori:adventure-platform-bukkit:4.3.3") implementation("net.kyori:adventure-text-minimessage:4.17.0") diff --git a/plugin/libs/Sparrow-Heart-0.17.jar b/plugin/libs/Sparrow-Heart-0.17.jar deleted file mode 100644 index dae4f422db277b374c2674ffb99bc56a9773fd51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147367 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 z 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

KW%K> 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

e{|Aqg|CHEMSCa|R=ZD(%2c7!2nv6ZwLQmZ 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ⅆ(M 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ⅇzN4yg*?+1|4)YL;k48r%Bt@b)+u)yD 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 literal 64703 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 crop_ids; @@ -58,14 +59,13 @@ public HarvestObjective(Instruction instruction) throws InstructionParseExceptio super(instruction, "crop_to_harvest"); crop_ids = new HashSet<>(); Collections.addAll(crop_ids, instruction.getArray()); - targetAmount = instruction.getVarNum(); - preCheckAmountNotLessThanOne(targetAmount); + targetAmount = instruction.getVarNum(VariableNumber.NOT_LESS_THAN_ONE_CHECKER); final QuestPackage pack = instruction.getPackage(); final String loc = instruction.getOptional("playerLocation"); final String range = instruction.getOptional("range"); if (loc != null && range != null) { - playerLocation = new CompoundLocation(pack, loc); - rangeVar = new VariableNumber(pack, range); + playerLocation = new VariableLocation(BetonQuest.getInstance().getVariableProcessor(), pack, loc); + rangeVar = new VariableNumber(BetonQuest.getInstance().getVariableProcessor(), pack, range); } else { playerLocation = null; rangeVar = null; @@ -100,12 +100,17 @@ private boolean isInvalidLocation(CropBreakEvent event, final Profile profile) { final Location targetLocation; try { - targetLocation = playerLocation.getLocation(profile); + targetLocation = playerLocation.getValue(profile); } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { LogUtils.warn(e.getMessage()); return true; } - final int range = rangeVar.getInt(profile); + int range; + try { + range = rangeVar.getValue(profile).intValue(); + } catch (QuestRuntimeException e) { + throw new RuntimeException(e); + } final Location playerLoc = event.getPlayer().getLocation(); return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range; } @@ -123,22 +128,21 @@ public void stop() { public static class PlantObjective extends CountingObjective implements Listener { - private final CompoundLocation playerLocation; + private final VariableLocation playerLocation; private final VariableNumber rangeVar; - private final HashSet loot_groups; + private final HashSet crops; public PlantObjective(Instruction instruction) throws InstructionParseException { super(instruction, "crop_to_plant"); - loot_groups = new HashSet<>(); - Collections.addAll(loot_groups, instruction.getArray()); - targetAmount = instruction.getVarNum(); - preCheckAmountNotLessThanOne(targetAmount); + crops = new HashSet<>(); + Collections.addAll(crops, instruction.getArray()); + targetAmount = instruction.getVarNum(VariableNumber.NOT_LESS_THAN_ONE_CHECKER); final QuestPackage pack = instruction.getPackage(); final String loc = instruction.getOptional("playerLocation"); final String range = instruction.getOptional("range"); if (loc != null && range != null) { - playerLocation = new CompoundLocation(pack, loc); - rangeVar = new VariableNumber(pack, range); + playerLocation = new VariableLocation(BetonQuest.getInstance().getVariableProcessor(), pack, loc); + rangeVar = new VariableNumber(BetonQuest.getInstance().getVariableProcessor(), pack, range); } else { playerLocation = null; rangeVar = null; @@ -154,7 +158,7 @@ public void onPlantCrop(CropPlantEvent event) { if (isInvalidLocation(event, onlineProfile)) { return; } - if (this.loot_groups.contains(event.getCrop().getKey()) && this.checkConditions(onlineProfile)) { + if (this.crops.contains(event.getCrop().getKey()) && this.checkConditions(onlineProfile)) { getCountingData(onlineProfile).progress(1); completeIfDoneOrNotify(onlineProfile); } @@ -167,12 +171,17 @@ private boolean isInvalidLocation(CropPlantEvent event, final Profile profile) { final Location targetLocation; try { - targetLocation = playerLocation.getLocation(profile); + targetLocation = playerLocation.getValue(profile); } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { LogUtils.warn(e.getMessage()); return true; } - final int range = rangeVar.getInt(profile); + int range; + try { + range = rangeVar.getValue(profile).intValue(); + } catch (QuestRuntimeException e) { + throw new RuntimeException(e); + } final Location playerLoc = event.getPlayer().getLocation(); return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java index f46538cfc..ee32d8ec3 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java @@ -17,27 +17,25 @@ package net.momirealms.customcrops.compatibility.season; -import net.advancedplugins.seasons.api.AdvancedSeasonsAPI; +import net.advancedplugins.seasons.Core; import net.momirealms.customcrops.api.integration.SeasonInterface; import net.momirealms.customcrops.api.mechanic.world.season.Season; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; public class AdvancedSeasonsImpl implements SeasonInterface { - private final AdvancedSeasonsAPI api; - - public AdvancedSeasonsImpl() { - this.api = new AdvancedSeasonsAPI(); - } - @Override - public Season getSeason(World world) { - return switch (api.getSeason(world)) { - case "SPRING" -> Season.SPRING; - case "WINTER" -> Season.WINTER; - case "SUMMER" -> Season.SUMMER; - case "FALL" -> Season.AUTUMN; - default -> null; + public Season getSeason(@NotNull World world) { + net.advancedplugins.seasons.enums.Season season = Core.getSeasonHandler().getSeason(world); + if (season == null) { + return null; + } + return switch (season.getType()) { + case SPRING -> Season.SPRING; + case WINTER -> Season.WINTER; + case SUMMER -> Season.SUMMER; + case FALL -> Season.AUTUMN; }; } @@ -54,7 +52,7 @@ public void setSeason(World world, Season season) { case SUMMER -> "SUMMER"; case SPRING -> "SPRING"; }; - api.setSeason(seasonName, world); + Core.getSeasonHandler().setSeason(net.advancedplugins.seasons.enums.Season.valueOf(seasonName), world.getName()); } @Override diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java index 6f76bf587..6f6c7e3df 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -63,7 +63,7 @@ public enum Dependency { COMMAND_API( "dev{}jorel", "commandapi-bukkit-shade", - "9.5.1", + "9.5.3", null, "commandapi-bukkit", Relocation.of("commandapi", "dev{}jorel{}commandapi") @@ -71,7 +71,7 @@ public enum Dependency { COMMAND_API_MOJMAP( "dev{}jorel", "commandapi-bukkit-shade-mojang-mapped", - "9.5.1", + "9.5.3", null, "commandapi-bukkit-mojang-mapped", Relocation.of("commandapi", "dev{}jorel{}commandapi") @@ -79,7 +79,7 @@ public enum Dependency { BOOSTED_YAML( "dev{}dejvokep", "boosted-yaml", - "1.3.4", + "1.3.6", null, "boosted-yaml", Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java index 28f2c8c1a..dc8ee83bd 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java @@ -155,9 +155,6 @@ private Path downloadDependency(Dependency dependency) throws DependencyDownload if (forceRepo == null) { // attempt to download the dependency from each repo in order. for (DependencyRepository repo : DependencyRepository.values()) { - if (repo.getId().equals("maven") && TimeZone.getDefault().getID().startsWith("Asia")) { - continue; - } try { LogUtils.info("Downloading dependency(" + fileName + ") from " + repo.getUrl() + dependency.getMavenRepoPath()); repo.download(dependency, file); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 0472a9360..035cd663c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -2141,6 +2141,7 @@ private void loadPot(String key, ConfigurationSection section) { int itemAmount = itemInHand.getAmount(); // get water in pot int waterInPot = plugin.getWorldManager().getPotAt(simpleLocation).map(WorldPot::getWater).orElse(0); + for (PassiveFillMethod method : pot.getPassiveFillMethods()) { if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { if (method.canFill(state)) { @@ -2162,7 +2163,7 @@ private void loadPot(String key, ConfigurationSection section) { plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), simpleLocation); } else { pot.trigger(ActionTrigger.FULL, state); - return FunctionResult.CANCEL_EVENT_AND_RETURN; + return FunctionResult.RETURN; } } return FunctionResult.RETURN; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index 7a96567be..542e17739 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -62,10 +62,11 @@ public WorldManagerImpl(CustomCropsPlugin plugin) { this.plugin = plugin; this.loadedWorlds = new ConcurrentHashMap<>(); this.worldSettingMap = new HashMap<>(); - try { - Class.forName("com.infernalsuite.aswm.api.world.SlimeWorld"); - this.worldAdaptor = new SlimeWorldAdaptor(this); - } catch (ClassNotFoundException ignore) { + if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldManager")) { + this.worldAdaptor = new SlimeWorldAdaptor(this, 1); + } else if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldPlugin")) { + this.worldAdaptor = new SlimeWorldAdaptor(this, 2); + } else { this.worldAdaptor = new BukkitWorldAdaptor(this); } } @@ -98,7 +99,7 @@ public void disable() { for (World world : Bukkit.getWorlds()) { unloadWorld(world); } - if (this.loadedWorlds.size() != 0) { + if (!this.loadedWorlds.isEmpty()) { LogUtils.severe("Detected that some worlds are not properly unloaded. " + "You can safely ignore this if you are editing \"worlds.list\" and restarting to apply it"); for (String world : this.loadedWorlds.keySet()) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java index 482a6aa7f..be3794506 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java @@ -276,7 +276,7 @@ public void onWorldLoad(WorldLoadEvent event) { } } - @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGH) + @EventHandler (ignoreCancelled = true, priority = EventPriority.LOW) public void onWorldUnload(WorldUnloadEvent event) { if (worldManager.isMechanicEnabled(event.getWorld())) worldManager.unloadWorld(event.getWorld()); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java index f72ffc06c..66345e5bb 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java @@ -37,20 +37,51 @@ import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.event.EventHandler; +import org.bukkit.plugin.Plugin; +import java.lang.reflect.Method; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.PriorityQueue; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; public class SlimeWorldAdaptor extends BukkitWorldAdaptor { - private final SlimePlugin slimePlugin; + private final Function getSlimeWorldFunction; - public SlimeWorldAdaptor(WorldManager worldManager) { + public SlimeWorldAdaptor(WorldManager worldManager, int version) { super(worldManager); - this.slimePlugin = (SlimePlugin) Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); + try { + if (version == 1) { + Plugin plugin = Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); + Class slimeClass = Class.forName("com.infernalsuite.aswm.api.SlimePlugin"); + Method method = slimeClass.getMethod("getWorld", String.class); + this.getSlimeWorldFunction = (name) -> { + try { + return (SlimeWorld) method.invoke(plugin, name); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }; + } else if (version == 2) { + Class apiClass = Class.forName("com.infernalsuite.aswm.api.AdvancedSlimePaperAPI"); + Object apiInstance = apiClass.getMethod("instance").invoke(null); + Method method = apiClass.getMethod("getLoadedWorld", String.class); + this.getSlimeWorldFunction = (name) -> { + try { + return (SlimeWorld) method.invoke(apiInstance, name); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }; + } else { + throw new IllegalArgumentException("Unsupported version: " + version); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } } @EventHandler (ignoreCancelled = true) @@ -67,7 +98,7 @@ public void onSlimeWorldLoad(LoadSlimeWorldEvent event) { @Override public void saveInfoData(CustomCropsWorld customCropsWorld) { - SlimeWorld slimeWorld = slimePlugin.getWorld(customCropsWorld.getWorldName()); + SlimeWorld slimeWorld = getSlimeWorldFunction.apply(customCropsWorld.getWorldName()); if (slimeWorld == null) { super.saveInfoData(customCropsWorld); return; @@ -85,7 +116,7 @@ public void saveInfoData(CustomCropsWorld customCropsWorld) { @Override public void unload(CustomCropsWorld customCropsWorld) { - SlimeWorld slimeWorld = slimePlugin.getWorld(customCropsWorld.getWorldName()); + SlimeWorld slimeWorld = getSlimeWorldFunction.apply(customCropsWorld.getWorldName()); if (slimeWorld == null) { super.unload(customCropsWorld); return; @@ -95,7 +126,7 @@ public void unload(CustomCropsWorld customCropsWorld) { @Override public void init(CustomCropsWorld customCropsWorld) { - SlimeWorld slimeWorld = slimePlugin.getWorld(customCropsWorld.getWorldName()); + SlimeWorld slimeWorld = getSlimeWorldFunction.apply(customCropsWorld.getWorldName()); if (slimeWorld == null) { super.init(customCropsWorld); return; @@ -117,7 +148,7 @@ public void init(CustomCropsWorld customCropsWorld) { @Override public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) { - SlimeWorld slimeWorld = slimePlugin.getWorld(customCropsWorld.getWorldName()); + SlimeWorld slimeWorld = getSlimeWorldFunction.apply(customCropsWorld.getWorldName()); if (slimeWorld == null) { super.loadChunkData(customCropsWorld, chunkPos); return; @@ -203,7 +234,7 @@ public void saveRegion(CustomCropsRegion customCropsRegion) { } private SlimeWorld getSlimeWorld(String name) { - return slimePlugin.getWorld(name); + return getSlimeWorldFunction.apply(name); } private CompoundTag chunkToTag(SerializableChunk serializableChunk) { @@ -212,7 +243,7 @@ private CompoundTag chunkToTag(SerializableChunk serializableChunk) { map.put(new IntTag("z", serializableChunk.getZ())); map.put(new IntTag("loadedSeconds", serializableChunk.getLoadedSeconds())); map.put(new LongTag("lastLoadedTime", serializableChunk.getLastLoadedTime())); - map.put(new IntArrayTag("queued", serializableChunk.getTicked())); + map.put(new IntArrayTag("queued", serializableChunk.getQueuedTasks())); map.put(new IntArrayTag("ticked", serializableChunk.getTicked())); CompoundMap sectionMap = new CompoundMap(); for (SerializableSection section : serializableChunk.getSections()) { From 617c2b16093775f1d13cca8b17874bd145d4c870 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 13 Aug 2024 15:40:26 +0800 Subject: [PATCH 060/329] Code clean up --- .../item/custom/AbstractCustomListener.java | 3 - .../compatibility/quest/BattlePassHook.java | 1 - .../dependencies/DependencyRepository.java | 1 - .../mechanic/action/ActionManagerImpl.java | 2 - .../mechanic/item/impl/WateringCanConfig.java | 1 - .../mechanic/world/WorldManagerImpl.java | 14 +++-- .../world/adaptor/SlimeWorldAdaptor.java | 57 +++++++------------ plugin/src/main/resources/plugin.yml | 1 + 8 files changed, 32 insertions(+), 48 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index 2d173e67e..b60ab4308 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -28,10 +28,7 @@ import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import net.momirealms.customcrops.api.mechanic.world.level.WorldGlass; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; import net.momirealms.customcrops.api.util.EventUtils; -import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java index f1fb6b7fe..2831efa2d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java @@ -26,7 +26,6 @@ import net.momirealms.customcrops.api.event.CropPlantEvent; import net.momirealms.customcrops.api.mechanic.item.Crop; import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java index 31ed18fb4..d41347f22 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java @@ -33,7 +33,6 @@ import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.TimeUnit; /** * Represents a repository which contains {@link Dependency}s. diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index da619f2be..3aef0467c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -17,8 +17,6 @@ package net.momirealms.customcrops.mechanic.action; -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java index 96caabf0e..537f05f88 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java @@ -17,7 +17,6 @@ package net.momirealms.customcrops.mechanic.item.impl; -import com.saicone.rtag.item.ItemObject; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ScoreComponent; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java index 542e17739..fa1d9d82b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -62,12 +62,16 @@ public WorldManagerImpl(CustomCropsPlugin plugin) { this.plugin = plugin; this.loadedWorlds = new ConcurrentHashMap<>(); this.worldSettingMap = new HashMap<>(); - if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldManager")) { + try { + // to ignore the warning from asp + Class.forName("com.infernalsuite.aswm.api.SlimePlugin"); this.worldAdaptor = new SlimeWorldAdaptor(this, 1); - } else if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldPlugin")) { - this.worldAdaptor = new SlimeWorldAdaptor(this, 2); - } else { - this.worldAdaptor = new BukkitWorldAdaptor(this); + } catch (ClassNotFoundException e) { + if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldPlugin")) { + this.worldAdaptor = new SlimeWorldAdaptor(this, 2); + } else { + this.worldAdaptor = new BukkitWorldAdaptor(this); + } } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java index 66345e5bb..679d85917 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java @@ -18,7 +18,6 @@ package net.momirealms.customcrops.mechanic.world.adaptor; import com.flowpowered.nbt.*; -import com.infernalsuite.aswm.api.SlimePlugin; import com.infernalsuite.aswm.api.events.LoadSlimeWorldEvent; import com.infernalsuite.aswm.api.world.SlimeWorld; import net.momirealms.customcrops.api.CustomCropsPlugin; @@ -84,6 +83,18 @@ public SlimeWorldAdaptor(WorldManager worldManager, int version) { } } + private CompoundMap createOrGetDataMap(SlimeWorld world) { + Optional optionalCompoundTag = world.getExtraData().getAsCompoundTag("customcrops"); + CompoundMap ccDataMap; + if (optionalCompoundTag.isEmpty()) { + ccDataMap = new CompoundMap(); + world.getExtraData().getValue().put(new CompoundTag("customcrops", ccDataMap)); + } else { + ccDataMap = optionalCompoundTag.get().getValue(); + } + return ccDataMap; + } + @EventHandler (ignoreCancelled = true) public void onSlimeWorldLoad(LoadSlimeWorldEvent event) { World world = Bukkit.getWorld(event.getSlimeWorld().getName()); @@ -103,14 +114,7 @@ public void saveInfoData(CustomCropsWorld customCropsWorld) { super.saveInfoData(customCropsWorld); return; } - - Optional optionalCompoundTag = slimeWorld.getExtraData().getAsCompoundTag("customcrops"); - if (optionalCompoundTag.isEmpty()) { - LogUtils.warn("Failed to unload data for world " + customCropsWorld.getWorldName() + " because slime world format is incorrect."); - return; - } - - CompoundMap ccDataMap = optionalCompoundTag.get().getValue(); + CompoundMap ccDataMap = createOrGetDataMap(slimeWorld); ccDataMap.put(new StringTag("world-info", gson.toJson(customCropsWorld.getInfoData()))); } @@ -132,15 +136,7 @@ public void init(CustomCropsWorld customCropsWorld) { return; } - Optional optionalCompoundTag = slimeWorld.getExtraData().getAsCompoundTag("customcrops"); - CompoundMap ccDataMap; - if (optionalCompoundTag.isEmpty()) { - ccDataMap = new CompoundMap(); - slimeWorld.getExtraData().getValue().put(new CompoundTag("customcrops", ccDataMap)); - } else { - ccDataMap = optionalCompoundTag.get().getValue(); - } - + CompoundMap ccDataMap = createOrGetDataMap(slimeWorld); String json = Optional.ofNullable(ccDataMap.get("world-info")).map(tag -> tag.getAsStringTag().get().getValue()).orElse(null); WorldInfoData data = (json == null || json.equals("null")) ? WorldInfoData.empty() : gson.fromJson(json, WorldInfoData.class); customCropsWorld.setInfoData(data); @@ -167,13 +163,8 @@ public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) return; } - Optional optionalCompoundTag = slimeWorld.getExtraData().getAsCompoundTag("customcrops"); - if (optionalCompoundTag.isEmpty()) { - LogUtils.warn("Failed to load data for " + chunkPos + " in world " + customCropsWorld.getWorldName() + " because slime world format is incorrect."); - return; - } - - Tag chunkTag = optionalCompoundTag.get().getValue().get(chunkPos.getAsString()); + CompoundMap ccDataMap = createOrGetDataMap(slimeWorld); + Tag chunkTag = ccDataMap.get(chunkPos.getAsString()); if (chunkTag == null) { return; } @@ -191,31 +182,27 @@ public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) @Override public void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk) { CustomCropsWorld customCropsWorld = customCropsChunk.getCustomCropsWorld(); - SlimeWorld slimeWorld = getSlimeWorld(customCropsChunk.getCustomCropsWorld().getWorldName()); + SlimeWorld slimeWorld = getSlimeWorld(customCropsWorld.getWorldName()); if (slimeWorld == null) { super.saveChunkToCachedRegion(customCropsChunk); return; } - Optional optionalCompoundTag = slimeWorld.getExtraData().getAsCompoundTag("customcrops"); - if (optionalCompoundTag.isEmpty()) { - LogUtils.warn("Failed to save data for " + customCropsChunk.getChunkPos() + " in world " + customCropsWorld.getWorldName() + " because slime world format is incorrect."); - return; - } + CompoundMap ccDataMap = createOrGetDataMap(slimeWorld); SerializableChunk serializableChunk = toSerializableChunk((CChunk) customCropsChunk); if (Bukkit.isPrimaryThread()) { if (serializableChunk.canPrune()) { - optionalCompoundTag.get().getValue().remove(customCropsChunk.getChunkPos().getAsString()); + ccDataMap.remove(customCropsChunk.getChunkPos().getAsString()); } else { - optionalCompoundTag.get().getValue().put(chunkToTag(serializableChunk)); + ccDataMap.put(chunkToTag(serializableChunk)); } } else { CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { if (serializableChunk.canPrune()) { - optionalCompoundTag.get().getValue().remove(customCropsChunk.getChunkPos().getAsString()); + ccDataMap.remove(customCropsChunk.getChunkPos().getAsString()); } else { - optionalCompoundTag.get().getValue().put(chunkToTag(serializableChunk)); + ccDataMap.put(chunkToTag(serializableChunk)); } }, null); } diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index a621d75ac..d16b5caeb 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -18,6 +18,7 @@ softdepend: - RealisticSeasons - AdvancedSeasons - SlimeWorldManager + - SlimeWorldPlugin - HuskClaims - HuskTowns - Residence From a5ebf3102d63ef1ffecfb98c5559ef596441389c Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sat, 17 Aug 2024 00:38:22 +0800 Subject: [PATCH 061/329] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 04f589950..1d1e764cd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ +# Note: This project is undergoing a major refactoring. Please switch to the 3.6-dev branch for the new API. + # Custom-Crops ![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/Xiao-MoMi/Custom-Crops) ![Code Size](https://img.shields.io/github/languages/code-size/Xiao-MoMi/Custom-Crops) ![bStats Servers](https://img.shields.io/bstats/servers/16593) ![bStats Players](https://img.shields.io/bstats/players/16593) +[![Scc Count Badge](https://sloc.xyz/github/Xiao-MoMi/Custom-Crops/?category=codes)](https://github.com/Xiao-MoMi/Custom-Crops/) ![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Crops) [![](https://jitpack.io/v/Xiao-MoMi/Custom-Crops.svg)](https://jitpack.io/#Xiao-MoMi/Custom-Crops) From 189efa37c1f555bbc4f0a3a98eed938a9bbe9b94 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sat, 24 Aug 2024 16:42:53 +0800 Subject: [PATCH 062/329] 3.5.10 --- .../api/mechanic/item/custom/AbstractCustomListener.java | 9 ++++++++- build.gradle.kts | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index b60ab4308..5c4bbe6f7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -28,6 +28,8 @@ import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.mechanic.world.level.WorldGlass; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; import net.momirealms.customcrops.api.util.EventUtils; import org.bukkit.Location; import org.bukkit.Material; @@ -164,7 +166,12 @@ public void onPlaceBlock(BlockPlaceEvent event) { final Location location = block.getLocation(); Optional customCropsBlock = CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(location)); if (customCropsBlock.isPresent()) { - CustomCropsPlugin.get().getWorldManager().removeAnythingAt(SimpleLocation.of(location)); + if (customCropsBlock.get() instanceof WorldPot || customCropsBlock.get() instanceof WorldGlass) { + CustomCropsPlugin.get().getWorldManager().removeAnythingAt(SimpleLocation.of(location)); + } else { + event.setCancelled(true); + return; + } } this.onPlaceBlock( event.getPlayer(), diff --git a/build.gradle.kts b/build.gradle.kts index 683b23a44..2edf59895 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.9" + project.version = "3.5.10" apply() apply(plugin = "java") From 3cbd1f65a6fc6b8fe6786beca7d892ba272d1bcd Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:49:09 +0800 Subject: [PATCH 063/329] fix issue caused by corrupted event --- .../item/custom/AbstractCustomListener.java | 13 ++++++------- build.gradle.kts | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index 5c4bbe6f7..e841c9985 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -86,7 +86,8 @@ public AbstractCustomListener(ItemManager itemManager) { Material.TWISTING_VINES, Material.WEEPING_VINES, Material.KELP, - Material.CACTUS + Material.CACTUS, + Material.AIR ) ); if (VersionManager.isHigherThan1_19()) { @@ -164,14 +165,12 @@ public void onBreakBlock(BlockBreakEvent event) { public void onPlaceBlock(BlockPlaceEvent event) { final Block block = event.getBlock(); final Location location = block.getLocation(); + if (CUSTOM_MATERIAL.contains(block.getType())) { + return; + } Optional customCropsBlock = CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(location)); if (customCropsBlock.isPresent()) { - if (customCropsBlock.get() instanceof WorldPot || customCropsBlock.get() instanceof WorldGlass) { - CustomCropsPlugin.get().getWorldManager().removeAnythingAt(SimpleLocation.of(location)); - } else { - event.setCancelled(true); - return; - } + CustomCropsPlugin.get().getWorldManager().removeAnythingAt(SimpleLocation.of(location)); } this.onPlaceBlock( event.getPlayer(), diff --git a/build.gradle.kts b/build.gradle.kts index 2edf59895..1c3a24125 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.10" + project.version = "3.5.11" apply() apply(plugin = "java") From bbde4ebd471e700a0442dfdf7da820202bf55815 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sat, 31 Aug 2024 22:57:45 +0800 Subject: [PATCH 064/329] 3.6 --- ...sic_Pack.zip => CustomCrops_Basic_Pack.zip | Bin README.md | 50 +- api/build.gradle.kts | 57 +- .../api/BukkitCustomCropsPlugin.java | 178 ++ .../customcrops/api/CustomCropsPlugin.java | 129 - .../api/action/AbstractActionManager.java | 891 ++++++ .../Variation.java => action/Action.java} | 23 +- .../api/action/ActionExpansion.java | 55 + .../ActionFactory.java} | 22 +- .../customcrops/api/action/ActionManager.java | 159 + .../customcrops/api/action/ActionTrigger.java | 10 +- .../customcrops/api/action/EmptyAction.java | 20 +- .../customcrops/api/common/Tuple.java | 59 - .../customcrops/api/common/item/KeyItem.java | 28 - .../api/context/AbstractContext.java | 64 + .../api/context/BlockContextImpl.java | 19 + .../customcrops/api/context/Context.java | 152 + .../customcrops/api/context/ContextKeys.java | 103 + .../api/context/PlayerContextImpl.java | 45 + .../api/core/AbstractCustomEventListener.java | 120 + .../api/core/AbstractItemManager.java | 55 + .../api/core/BuiltInBlockMechanics.java | 43 + .../api/core/BuiltInItemMechanics.java | 30 + .../api/core/ClearableMappedRegistry.java | 17 + .../api/core/ClearableRegistry.java | 6 + .../customcrops/api/core/ConfigManager.java | 422 +++ .../customcrops/api/core/CustomForm.java | 22 + .../api/core/CustomItemProvider.java | 34 + .../customcrops/api/core/ExistenceForm.java | 8 + .../api/core/FurnitureRotation.java | 79 + .../customcrops/api/core/IdMap.java | 32 + .../api/core/InteractionResult.java | 8 + .../customcrops/api/core/ItemManager.java | 63 + .../customcrops/api/core/MappedRegistry.java | 58 + .../customcrops/api/core/Registries.java | 42 + .../customcrops/api/core/Registry.java | 16 + .../customcrops/api/core/RegistryAccess.java | 21 + .../api/core/SimpleRegistryAccess.java | 54 + .../customcrops/api/core/StatedItem.java | 9 + .../SynchronizedCompoundMap.java | 21 +- .../api/core/WriteableRegistry.java | 6 + .../core/block/AbstractCustomCropsBlock.java | 82 + .../item => core/block}/BoneMeal.java | 72 +- .../api/core/block/BreakReason.java | 12 + .../customcrops/api/core/block/CropBlock.java | 398 +++ .../api/core/block/CropConfig.java | 104 + .../api/core/block/CropConfigImpl.java | 345 ++ .../api/core/block/CropStageConfig.java | 61 + .../api/core/block/CropStageConfigImpl.java | 176 ++ .../api/core/block/CrowAttack.java | 115 + .../api/core/block/CustomCropsBlock.java | 29 + .../api/core/block/DeathCondition.java | 40 + .../api/core/block/GreenhouseBlock.java | 96 + .../api/core/block/GrowCondition.java | 25 + .../customcrops/api/core/block/PotBlock.java | 574 ++++ .../customcrops/api/core/block/PotConfig.java | 103 + .../api/core/block/PotConfigImpl.java | 339 ++ .../api/core/block/ScarecrowBlock.java | 94 + .../api/core/block/SprinklerBlock.java | 310 ++ .../api/core/block/SprinklerConfig.java | 148 + .../api/core/block/SprinklerConfigImpl.java | 375 +++ .../api/core/block/VariationData.java | 20 +- .../core/item/AbstractCustomCropsItem.java | 29 + .../core/item/AbstractFertilizerConfig.java | 121 + .../api/core/item/CustomCropsItem.java | 15 + .../customcrops/api/core/item/Fertilizer.java | 40 + .../api/core/item/FertilizerConfig.java | 44 + .../api/core/item/FertilizerImpl.java | 51 + .../api/core/item/FertilizerItem.java | 113 + .../api/core/item/FertilizerType.java | 150 + .../customcrops/api/core/item/Quality.java | 31 + .../api/core/item/QualityImpl.java | 52 + .../customcrops/api/core/item/SeedItem.java | 130 + .../customcrops/api/core/item/SoilRetain.java | 28 + .../api/core/item/SoilRetainImpl.java | 44 + .../customcrops/api/core/item/SpeedGrow.java | 30 + .../api/core/item/SpeedGrowImpl.java | 51 + .../api/core/item/SprinklerItem.java | 111 + .../customcrops/api/core/item/Variation.java | 31 + .../api/core/item/VariationImpl.java | 56 + .../api/core/item/WateringCanConfig.java | 108 + .../api/core/item/WateringCanConfigImpl.java | 342 ++ .../api/core/item/WateringCanItem.java | 358 +++ .../api/core/item/YieldIncrease.java | 30 + .../api/core/item/YieldIncreaseImpl.java | 51 + .../api/core/water/AbstractMethod.java | 50 + .../water/FillMethod.java} | 11 +- .../water/WateringMethod.java} | 19 +- .../{mechanic => core}/world/BlockPos.java | 36 +- .../{mechanic => core}/world/ChunkPos.java | 22 +- .../api/core/world/CustomCropsBlockState.java | 15 + .../core/world/CustomCropsBlockStateImpl.java | 52 + .../api/core/world/CustomCropsChunk.java | 195 ++ .../api/core/world/CustomCropsChunkImpl.java | 288 ++ .../api/core/world/CustomCropsRegion.java | 36 +- .../api/core/world/CustomCropsRegionImpl.java | 86 + .../api/core/world/CustomCropsSection.java | 52 + .../core/world/CustomCropsSectionImpl.java | 61 + .../api/core/world/CustomCropsWorld.java | 215 ++ .../api/core/world/CustomCropsWorldImpl.java | 474 +++ .../world/DataBlock.java} | 19 +- .../api/core/world/DelayedTickTask.java | 11 +- .../customcrops/api/core/world/Pos3.java | 87 + .../{mechanic => core}/world/RegionPos.java | 5 +- .../customcrops/api/core/world/Season.java | 53 + .../api/core/world/SerializableChunk.java | 12 +- .../world/SerializableSection.java} | 17 +- .../world/WorldExtraData.java} | 32 +- .../api/core/world/WorldManager.java | 31 + .../level => core/world}/WorldSetting.java | 36 +- .../world/adaptor/AbstractWorldAdaptor.java | 124 + .../api/core/world/adaptor/WorldAdaptor.java | 45 + .../api/core/wrapper/WrappedBreakEvent.java | 103 + .../core/wrapper/WrappedInteractAirEvent.java | 43 + .../core/wrapper/WrappedInteractEvent.java | 94 + .../api/core/wrapper/WrappedPlaceEvent.java | 67 + .../api/event/BoneMealUseEvent.java | 49 +- .../customcrops/api/event/CropBreakEvent.java | 136 +- .../api/event/CropInteractEvent.java | 46 +- .../customcrops/api/event/CropPlantEvent.java | 30 +- .../api/event/CustomCropsReloadEvent.java | 10 +- .../api/event/FertilizerUseEvent.java | 37 +- .../api/event/GreenhouseGlassBreakEvent.java | 54 +- ...java => GreenhouseGlassInteractEvent.java} | 78 +- .../api/event/GreenhouseGlassPlaceEvent.java | 19 +- .../customcrops/api/event/PotBreakEvent.java | 62 +- .../customcrops/api/event/PotFillEvent.java | 59 +- .../api/event/PotInteractEvent.java | 30 +- .../customcrops/api/event/PotPlaceEvent.java | 44 +- .../api/event/ScarecrowBreakEvent.java | 49 +- .../api/event/ScarecrowInteractEvent.java | 114 + .../api/event/ScarecrowPlaceEvent.java | 19 +- .../api/event/SeasonChangeEvent.java | 73 - .../api/event/SprinklerBreakEvent.java | 58 +- .../api/event/SprinklerFillEvent.java | 73 +- .../api/event/SprinklerInteractEvent.java | 35 +- .../api/event/SprinklerPlaceEvent.java | 35 +- .../api/event/WateringCanFillEvent.java | 34 +- ...ent.java => WateringCanWaterPotEvent.java} | 67 +- .../event/WateringCanWaterSprinklerEvent.java | 119 + .../ExternalProvider.java} | 17 +- .../api/integration/IntegrationManager.java | 78 + .../api/integration/ItemLibrary.java | 49 - .../api/integration/ItemProvider.java | 49 + ...velInterface.java => LevelerProvider.java} | 15 +- ...asonInterface.java => SeasonProvider.java} | 43 +- .../api/manager/ActionManager.java | 101 - .../api/manager/AdventureManager.java | 63 - .../api/manager/ConditionManager.java | 106 - .../api/manager/ConfigManager.java | 185 -- .../api/manager/IntegrationManager.java | 58 - .../customcrops/api/manager/ItemManager.java | 413 --- .../api/manager/MessageManager.java | 54 - .../api/manager/PlaceholderManager.java | 47 - .../api/manager/RequirementManager.java | 110 - .../api/manager/VersionManager.java | 91 - .../customcrops/api/manager/WorldManager.java | 420 --- .../api/mechanic/action/Action.java | 30 - .../api/mechanic/action/ActionExpansion.java | 49 - .../api/mechanic/action/ActionTrigger.java | 36 - .../api/mechanic/condition/Condition.java | 32 - .../condition/ConditionExpansion.java | 49 - .../mechanic/condition/ConditionFactory.java | 29 - .../api/mechanic/condition/Conditions.java | 19 - .../mechanic/condition/DeathConditions.java | 46 - .../customcrops/api/mechanic/item/Crop.java | 206 -- .../api/mechanic/item/Fertilizer.java | 80 - .../api/mechanic/item/FertilizerType.java | 26 - .../api/mechanic/item/ItemCarrier.java | 27 - .../api/mechanic/item/ItemType.java | 26 - .../customcrops/api/mechanic/item/Pot.java | 137 - .../api/mechanic/item/Scarecrow.java | 30 - .../api/mechanic/item/Sprinkler.java | 134 - .../api/mechanic/item/WateringCan.java | 138 - .../item/custom/AbstractCustomListener.java | 349 --- .../mechanic/item/custom/CustomProvider.java | 132 - .../mechanic/item/fertilizer/SpeedGrow.java | 30 - .../item/fertilizer/YieldIncrease.java | 30 - .../item/water/AbstractFillMethod.java | 65 - .../api/mechanic/misc/CRotation.java | 58 - .../api/mechanic/misc/MatchRule.java | 8 - .../customcrops/api/mechanic/misc/Reason.java | 29 - .../customcrops/api/mechanic/misc/Value.java | 31 - .../api/mechanic/requirement/Requirement.java | 29 - .../requirement/RequirementFactory.java | 45 - .../api/mechanic/requirement/State.java | 73 - .../mechanic/world/AbstractWorldAdaptor.java | 49 - .../api/mechanic/world/CustomCropsBlock.java | 31 - .../api/mechanic/world/SimpleLocation.java | 147 - .../api/mechanic/world/Tickable.java | 12 - .../world/level/AbstractCustomCropsBlock.java | 105 - .../world/level/CustomCropsChunk.java | 344 -- .../world/level/CustomCropsRegion.java | 76 - .../world/level/CustomCropsSection.java | 81 - .../world/level/CustomCropsWorld.java | 412 --- .../api/mechanic/world/level/DataBlock.java | 55 - .../api/mechanic/world/level/WorldCrop.java | 52 - .../api/mechanic/world/level/WorldGlass.java | 23 - .../api/mechanic/world/level/WorldPot.java | 94 - .../mechanic/world/level/WorldScarecrow.java | 23 - .../mechanic/world/level/WorldSprinkler.java | 52 - .../misc/image => misc}/WaterBar.java | 2 +- .../cooldown}/CoolDownManager.java | 50 +- .../placeholder/BukkitPlaceholderManager.java | 145 + .../misc/placeholder/PlaceholderAPIUtils.java | 51 + .../misc/placeholder/PlaceholderManager.java | 88 + .../api/misc/value/DynamicText.java | 76 + .../misc/value/ExpressionMathValueImpl.java | 41 + .../customcrops/api/misc/value/MathValue.java | 123 + .../misc/value/PlaceholderTextValueImpl.java | 42 + .../api/misc/value/PlainMathValueImpl.java | 13 +- .../api/misc/value/PlainTextValueImpl.java | 18 +- .../api/misc/value/RangedDoubleValueImpl.java | 41 + .../api/misc/value/RangedIntValueImpl.java | 41 + .../customcrops/api/misc/value/TextValue.java | 96 + .../AbstractRequirementManager.java | 1118 +++++++ .../api}/requirement/EmptyRequirement.java | 18 +- .../api/requirement/Requirement.java | 41 + .../requirement/RequirementExpansion.java | 28 +- .../api/requirement/RequirementFactory.java | 50 + .../api/requirement/RequirementManager.java | 133 + .../api/scheduler/CancellableTask.java | 33 - .../customcrops/api/scheduler/Scheduler.java | 104 - .../api/util/DisplayEntityUtils.java | 15 - .../customcrops/api/util/EventUtils.java | 18 +- .../customcrops/api/util/FakeCancellable.java | 18 + .../customcrops/api/util/InventoryUtils.java | 108 + .../customcrops/api/util/LocationUtils.java | 13 +- .../customcrops/api/util/LogUtils.java | 78 - .../customcrops/api/util/MoonPhase.java | 43 +- .../customcrops/api}/util/ParticleUtils.java | 2 +- .../customcrops/api/util/PlayerUtils.java | 158 + .../customcrops/api/util/PluginUtils.java | 20 + .../customcrops/api/util/StringUtils.java | 10 + build.gradle.kts | 98 +- common/build.gradle.kts | 39 + .../common/annotation/DoNotUse.java | 11 + .../command/AbstractCommandFeature.java | 85 + .../command/AbstractCommandManager.java | 179 ++ .../common/command/CommandBuilder.java | 58 + .../common/command/CommandConfig.java | 77 + .../common/command/CommandFeature.java | 43 + .../command/CustomCropsCommandManager.java | 56 + .../common/config/ConfigLoader.java | 69 + .../common/dependency/Dependency.java | 268 ++ .../DependencyDownloadException.java | 2 +- .../common/dependency}/DependencyManager.java | 2 +- .../dependency}/DependencyManagerImpl.java | 102 +- .../dependency}/DependencyRegistry.java | 4 +- .../dependency}/DependencyRepository.java | 31 +- .../classloader/IsolatedClassLoader.java | 2 +- .../dependency}/relocation/Relocation.java | 2 +- .../relocation/RelocationHandler.java | 13 +- .../relocation/RelocationHelper.java | 3 +- .../common/helper/AdventureHelper.java | 285 ++ .../common/helper/ExpressionHelper.java | 27 +- .../customcrops/common/helper/GsonHelper.java | 59 + .../common/helper/VersionHelper.java | 158 +- .../common/item}/AbstractItem.java | 55 +- .../common/item/ComponentKeys.java | 32 + .../customcrops/common/item/Item.java | 157 + .../customcrops/common/item}/ItemFactory.java | 37 +- .../locale/CustomCropsCaptionFormatter.java | 35 + .../common/locale/CustomCropsCaptionKeys.java | 18 +- .../locale/CustomCropsCaptionProvider.java | 37 + .../common/locale/MessageConstants.java | 32 + .../MiniMessageTranslationRegistry.java | 73 + .../MiniMessageTranslationRegistryImpl.java | 235 ++ .../common/locale/MiniMessageTranslator.java | 47 + .../locale/MiniMessageTranslatorImpl.java | 92 + .../common/locale/TranslationManager.java | 201 ++ .../common/plugin/CustomCropsPlugin.java | 138 + .../common/plugin/CustomCropsProperties.java | 61 + .../plugin}/classpath/ClassPathAppender.java | 2 +- .../ReflectionClassPathAppender.java | 8 +- .../classpath/URLClassLoaderAccess.java | 12 +- .../common/plugin/feature}/Reloadable.java | 22 +- .../plugin/logging/JavaPluginLogger.java | 50 +- .../plugin/logging/Log4jPluginLogger.java | 61 + .../common/plugin/logging/PluginLogger.java | 23 +- .../plugin/logging/Slf4jPluginLogger.java | 61 + .../scheduler/AbstractJavaScheduler.java | 151 + .../plugin/scheduler/RegionExecutor.java | 33 + .../plugin/scheduler/SchedulerAdapter.java | 111 + .../plugin/scheduler/SchedulerTask.java | 16 +- .../common/sender/AbstractSender.java | 116 + .../common/sender/DummyConsoleSender.java | 62 + .../customcrops/common/sender/Sender.java | 121 + .../common/sender/SenderFactory.java | 82 + .../customcrops/common/util/ArrayUtils.java | 102 + .../customcrops/common}/util/ClassUtils.java | 58 +- .../common/util/CompletableFutures.java | 73 + .../customcrops/common/util/Either.java | 113 + .../customcrops/common/util/EitherImpl.java | 130 + .../customcrops/common/util/FileUtils.java | 112 + .../customcrops/common/util/Key.java | 56 + .../customcrops/common/util/ListUtils.java | 49 + .../customcrops/common/util/Pair.java | 90 + .../customcrops/common/util/RandomUtils.java | 140 + .../customcrops/common/util/TriConsumer.java | 13 +- .../customcrops/common/util/TriFunction.java | 17 +- .../customcrops/common/util/Tristate.java | 98 + .../customcrops/common/util/Tuple.java | 114 + .../customcrops/common/util/UUIDUtils.java | 87 + .../customcrops/common/util/WeightUtils.java | 108 + .../main/resources/custom-crops.properties | 20 + compatibility-asp-r1/build.gradle.kts | 27 + .../adaptor/asp_r1/SlimeWorldAdaptorR1.java | 227 ++ .../build.gradle.kts | 20 +- .../itemsadder_r1/ItemsAdderListener.java | 79 + .../itemsadder_r1/ItemsAdderProvider.java | 104 + .../.gitignore | 0 compatibility-oraxen-r1/build.gradle.kts | 25 + .../custom/oraxen_r1/OraxenListener.java | 118 + .../custom/oraxen_r1/OraxenProvider.java | 97 + .../.gitignore | 0 .../build.gradle.kts | 7 + .../custom/oraxen_r2/OraxenListener.java | 118 + .../custom/oraxen_r2/OraxenProvider.java | 97 + compatibility/build.gradle.kts | 72 + .../libs/AdvancedSeasons-API.jar | Bin .../libs/BattlePass-4.0.6-api.jar | Bin .../libs/ClueScrolls-api.jar | Bin .../libs/RealisticSeasons-api.jar | Bin {plugin => compatibility}/libs/mcMMO-api.jar | Bin .../libs/zaphkiel-2.0.24.jar | Bin .../bukkit/integration/VaultHook.java | 79 + .../item/CustomFishingItemProvider.java | 57 + .../item/MMOItemsItemProvider.java | 22 +- .../item/MythicMobsItemProvider.java | 23 +- .../item/NeigeItemsItemProvider.java | 20 +- .../item/ZaphkielItemProvider.java | 24 +- .../level/AuraSkillsLevelerProvider.java | 18 +- .../level/AureliumSkillsProvider.java | 27 +- .../level/EcoJobsLevelerProvider.java | 18 +- .../level/EcoSkillsLevelerProvider.java | 18 +- .../level/JobsRebornLevelerProvider.java | 28 +- .../level/MMOCoreLevelerProvider.java | 18 +- .../level/McMMOLevelerProvider.java | 18 +- .../integration/papi/CustomCropsPapi.java | 81 + .../season/AdvancedSeasonsProvider.java | 33 +- .../season/RealisticSeasonsProvider.java | 44 + gradle.properties | 50 +- gradle/wrapper/gradle-wrapper.properties | 2 +- legacy-api/build.gradle.kts | 16 - .../customcrops/api/object/ItemMode.java | 31 - .../customcrops/api/object/ItemType.java | 30 - .../api/object/OfflineReplaceTask.java | 33 - .../api/object/crop/GrowingCrop.java | 48 - .../api/object/fertilizer/Fertilizer.java | 44 - .../customcrops/api/object/pot/Pot.java | 52 - .../api/object/sprinkler/Sprinkler.java | 48 - .../customcrops/api/object/world/CCChunk.java | 77 - .../api/object/world/ChunkCoordinate.java | 72 - .../api/object/world/SimpleLocation.java | 108 - .../item/custom/oraxen/OraxenListener.java | 108 - .../item/custom/oraxen/OraxenProvider.java | 104 - oraxen-legacy/.gitignore | 42 - .../oraxenlegacy/LegacyOraxenListener.java | 108 - .../oraxenlegacy/LegacyOraxenProvider.java | 104 - plugin/build.gradle.kts | 120 +- plugin/libs/Sparrow-Heart-0.19.jar | Bin 177287 -> 0 bytes .../customcrops/CustomCropsPluginImpl.java | 192 -- .../BukkitBootstrap.java} | 28 +- .../bukkit/BukkitCustomCropsPluginImpl.java | 223 ++ .../bukkit/action/BlockActionManager.java | 110 + .../bukkit/action/PlayerActionManager.java | 524 ++++ .../bukkit/command/BukkitCommandFeature.java | 61 + .../bukkit/command/BukkitCommandManager.java | 70 + .../command/feature/DebugDataCommand.java | 73 + .../bukkit/command/feature/ReloadCommand.java | 50 + .../bukkit/config/BukkitConfigManager.java | 249 ++ .../customcrops/bukkit/config/ConfigType.java | 320 ++ .../integration/BukkitIntegrationManager.java | 164 + .../adaptor/BukkitWorldAdaptor.java | 424 +++ .../item}/BukkitItemFactory.java | 13 +- .../bukkit/item/BukkitItemManager.java | 442 +++ .../item}/ComponentItemFactory.java | 7 +- .../item}/UniversalItemFactory.java | 7 +- .../bukkit/misc/HologramManager.java | 172 + .../requirement/BlockRequirementManager.java | 52 + .../requirement/PlayerRequirementManager.java | 247 ++ .../scheduler/BukkitSchedulerAdapter.java | 53 + .../bukkit/scheduler/DummyTask.java | 15 + .../bukkit/scheduler/impl/BukkitExecutor.java | 95 + .../bukkit/scheduler/impl/FoliaExecutor.java | 100 + .../bukkit/sender/BukkitSenderFactory.java | 112 + .../bukkit/world/BukkitWorldManager.java | 327 ++ .../compatibility/IntegrationManagerImpl.java | 153 - .../customcrops/compatibility/VaultHook.java | 40 - .../compatibility/papi/CCPapi.java | 103 - .../compatibility/papi/ParseUtils.java | 33 - .../compatibility/quest/BattlePassHook.java | 91 - .../compatibility/quest/BetonQuestHook.java | 199 -- .../compatibility/quest/ClueScrollsHook.java | 67 - .../compatibility/season/InBuiltSeason.java | 74 - .../season/RealisticSeasonsImpl.java | 67 - .../libraries/dependencies/Dependency.java | 211 -- .../libraries/loader/JarInJarClassLoader.java | 155 - .../manager/AdventureManagerImpl.java | 214 -- .../customcrops/manager/CommandManager.java | 290 -- .../manager/ConfigManagerImpl.java | 277 -- .../customcrops/manager/HologramManager.java | 182 -- .../manager/MessageManagerImpl.java | 99 - .../customcrops/manager/PacketManager.java | 64 - .../manager/PlaceholderManagerImpl.java | 120 - .../mechanic/action/ActionManagerImpl.java | 1206 ------- .../condition/ConditionManagerImpl.java | 760 ----- .../mechanic/item/AbstractEventItem.java | 43 - .../mechanic/item/ItemManagerImpl.java | 2766 ----------------- .../custom/crucible/CrucibleListener.java | 49 - .../custom/crucible/CrucibleProvider.java | 124 - .../custom/itemsadder/ItemsAdderListener.java | 94 - .../custom/itemsadder/ItemsAdderProvider.java | 115 - .../mechanic/item/factory/ComponentKeys.java | 17 - .../mechanic/item/factory/Item.java | 39 - .../mechanic/item/function/CFunction.java | 76 - .../item/function/wrapper/BreakWrapper.java | 35 - .../function/wrapper/ConditionWrapper.java | 40 - .../wrapper/InteractBlockWrapper.java | 42 - .../wrapper/InteractFurnitureWrapper.java | 46 - .../function/wrapper/InteractWrapper.java | 35 - .../function/wrapper/PlaceBlockWrapper.java | 36 - .../item/function/wrapper/PlaceWrapper.java | 42 - .../item/impl/AbstractFertilizer.java | 102 - .../mechanic/item/impl/CropConfig.java | 241 -- .../mechanic/item/impl/PotConfig.java | 177 -- .../mechanic/item/impl/SprinklerConfig.java | 165 - .../mechanic/item/impl/WateringCanConfig.java | 212 -- .../impl/fertilizer/QualityCropConfig.java | 62 - .../impl/fertilizer/SoilRetainConfig.java | 53 - .../item/impl/fertilizer/SpeedGrowConfig.java | 60 - .../item/impl/fertilizer/VariationConfig.java | 53 - .../impl/fertilizer/YieldIncreaseConfig.java | 60 - .../mechanic/misc/CrowAttackAnimation.java | 97 - .../mechanic/misc/TempFakeItem.java | 76 - .../mechanic/misc/migrator/Migration.java | 472 --- .../requirement/RequirementManagerImpl.java | 1041 ------- .../customcrops/mechanic/world/CChunk.java | 624 ---- .../customcrops/mechanic/world/CRegion.java | 83 - .../customcrops/mechanic/world/CSection.java | 76 - .../customcrops/mechanic/world/CWorld.java | 590 ---- .../mechanic/world/SerializableChunk.java | 73 - .../mechanic/world/SerializableSection.java | 41 - .../mechanic/world/WorldManagerImpl.java | 599 ---- .../world/adaptor/BukkitWorldAdaptor.java | 729 ----- .../world/adaptor/SlimeWorldAdaptor.java | 323 -- .../mechanic/world/block/MemoryCrop.java | 181 -- .../mechanic/world/block/MemoryGlass.java | 60 - .../mechanic/world/block/MemoryPot.java | 272 -- .../mechanic/world/block/MemoryScarecrow.java | 60 - .../mechanic/world/block/MemorySprinkler.java | 183 -- .../scheduler/BukkitSchedulerImpl.java | 83 - .../scheduler/FoliaSchedulerImpl.java | 87 - .../customcrops/scheduler/SchedulerImpl.java | 141 - .../customcrops/scheduler/SyncScheduler.java | 33 - .../scheduler/task/ReplaceTask.java | 46 - .../customcrops/util/ConfigUtils.java | 415 --- .../customcrops/util/FakeEntityUtils.java | 147 - .../customcrops/util/ItemUtils.java | 116 - .../customcrops/util/RotationUtils.java | 64 - plugin/src/main/resources/commands.yml | 38 + plugin/src/main/resources/config.yml | 52 +- .../main/resources/contents/crops/default.yml | 19 +- .../main/resources/contents/pots/default.yml | 7 +- plugin/src/main/resources/messages/en.yml | 8 - plugin/src/main/resources/messages/es.yml | 8 - plugin/src/main/resources/messages/fr.yml | 8 - plugin/src/main/resources/messages/pt.yml | 8 - plugin/src/main/resources/messages/ru.yml | 8 - plugin/src/main/resources/messages/zh_cn.yml | 8 - plugin/src/main/resources/plugin.yml | 9 +- plugin/src/main/resources/translations/en.yml | 46 + .../src/main/resources/translations/zh_cn.yml | 46 + settings.gradle.kts | 12 + 475 files changed, 24699 insertions(+), 24307 deletions(-) rename CustomCrops_3.4_Basic_Pack.zip => CustomCrops_Basic_Pack.zip (100%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/item/fertilizer/Variation.java => action/Action.java} (53%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/ActionExpansion.java rename api/src/main/java/net/momirealms/customcrops/api/{common/item/EventItem.java => action/ActionFactory.java} (58%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/ActionManager.java rename plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionTrigger.java => api/src/main/java/net/momirealms/customcrops/api/action/ActionTrigger.java (79%) rename plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java => api/src/main/java/net/momirealms/customcrops/api/action/EmptyAction.java (58%) delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/common/Tuple.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/context/AbstractContext.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/context/Context.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/context/PlayerContextImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/CustomForm.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/ExistenceForm.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/Registries.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/Registry.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/world => core}/SynchronizedCompoundMap.java (87%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/item => core/block}/BoneMeal.java (55%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/BreakReason.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java rename plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/VariationCrop.java => api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java (67%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/water/AbstractMethod.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/item/water/PositiveFillMethod.java => core/water/FillMethod.java} (72%) rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/item/water/PassiveFillMethod.java => core/water/WateringMethod.java} (76%) rename api/src/main/java/net/momirealms/customcrops/api/{mechanic => core}/world/BlockPos.java (68%) rename api/src/main/java/net/momirealms/customcrops/api/{mechanic => core}/world/ChunkPos.java (77%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockStateImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java rename plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakBlockWrapper.java => api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java (58%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSectionImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/item/fertilizer/SoilRetain.java => core/world/DataBlock.java} (65%) rename plugin/src/main/java/net/momirealms/customcrops/scheduler/task/TickTask.java => api/src/main/java/net/momirealms/customcrops/api/core/world/DelayedTickTask.java (80%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/Pos3.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic => core}/world/RegionPos.java (95%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/Season.java rename plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceFurnitureWrapper.java => api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableChunk.java (67%) rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/world/season/Season.java => core/world/SerializableSection.java} (72%) rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/world/level/WorldInfoData.java => core/world/WorldExtraData.java} (69%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/world/level => core/world}/WorldSetting.java (86%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/AbstractWorldAdaptor.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedBreakEvent.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractAirEvent.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractEvent.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedPlaceEvent.java rename api/src/main/java/net/momirealms/customcrops/api/event/{BoneMealDispenseEvent.java => GreenhouseGlassInteractEvent.java} (57%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java rename api/src/main/java/net/momirealms/customcrops/api/event/{WateringCanWaterEvent.java => WateringCanWaterPotEvent.java} (62%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/action/ActionFactory.java => integration/ExternalProvider.java} (64%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/integration/IntegrationManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/integration/ItemProvider.java rename api/src/main/java/net/momirealms/customcrops/api/integration/{LevelInterface.java => LevelerProvider.java} (64%) rename api/src/main/java/net/momirealms/customcrops/api/integration/{SeasonInterface.java => SeasonProvider.java} (53%) delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/AdventureManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/MessageManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/PlaceholderManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionTrigger.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/FertilizerType.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemCarrier.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemType.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/AbstractFillMethod.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/CRotation.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/MatchRule.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Reason.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/AbstractWorldAdaptor.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsRegion.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldGlass.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldScarecrow.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic/misc/image => misc}/WaterBar.java (95%) rename api/src/main/java/net/momirealms/customcrops/api/{manager => misc/cooldown}/CoolDownManager.java (54%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/BukkitPlaceholderManager.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/PlaceholderAPIUtils.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/PlaceholderManager.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/value/DynamicText.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/value/ExpressionMathValueImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/value/MathValue.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/value/PlaceholderTextValueImpl.java rename plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/PlainValue.java => api/src/main/java/net/momirealms/customcrops/api/misc/value/PlainMathValueImpl.java (71%) rename plugin/src/main/java/net/momirealms/customcrops/mechanic/action/EmptyAction.java => api/src/main/java/net/momirealms/customcrops/api/misc/value/PlainTextValueImpl.java (64%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/value/RangedDoubleValueImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/value/RangedIntValueImpl.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/value/TextValue.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java rename {plugin/src/main/java/net/momirealms/customcrops/mechanic => api/src/main/java/net/momirealms/customcrops/api}/requirement/EmptyRequirement.java (60%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/requirement/Requirement.java rename api/src/main/java/net/momirealms/customcrops/api/{mechanic => }/requirement/RequirementExpansion.java (53%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementFactory.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementManager.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/scheduler/CancellableTask.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/scheduler/Scheduler.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/DisplayEntityUtils.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/FakeCancellable.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/InventoryUtils.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/LogUtils.java rename plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakFurnitureWrapper.java => api/src/main/java/net/momirealms/customcrops/api/util/MoonPhase.java (51%) rename {plugin/src/main/java/net/momirealms/customcrops => api/src/main/java/net/momirealms/customcrops/api}/util/ParticleUtils.java (96%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/PlayerUtils.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/PluginUtils.java create mode 100644 common/build.gradle.kts create mode 100644 common/src/main/java/net/momirealms/customcrops/common/annotation/DoNotUse.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/command/AbstractCommandFeature.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/command/AbstractCommandManager.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/command/CommandBuilder.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/command/CommandConfig.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/command/CommandFeature.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/command/CustomCropsCommandManager.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/config/ConfigLoader.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/dependency/Dependency.java rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/DependencyDownloadException.java (96%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/DependencyManager.java (96%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/DependencyManagerImpl.java (69%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/DependencyRegistry.java (93%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/DependencyRepository.java (86%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/classloader/IsolatedClassLoader.java (96%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/relocation/Relocation.java (97%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/relocation/RelocationHandler.java (89%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies => common/src/main/java/net/momirealms/customcrops/common/dependency}/relocation/RelocationHelper.java (95%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/helper/AdventureHelper.java rename api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java => common/src/main/java/net/momirealms/customcrops/common/helper/ExpressionHelper.java (55%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/helper/GsonHelper.java rename plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java => common/src/main/java/net/momirealms/customcrops/common/helper/VersionHelper.java (64%) rename {plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory => common/src/main/java/net/momirealms/customcrops/common/item}/AbstractItem.java (69%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/item/ComponentKeys.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/item/Item.java rename {plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory => common/src/main/java/net/momirealms/customcrops/common/item}/ItemFactory.java (55%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionFormatter.java rename api/src/main/java/net/momirealms/customcrops/api/common/Initable.java => common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionKeys.java (56%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionProvider.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslationRegistry.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslationRegistryImpl.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslator.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslatorImpl.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/locale/TranslationManager.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/plugin/CustomCropsPlugin.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/plugin/CustomCropsProperties.java rename {plugin/src/main/java/net/momirealms/customcrops/libraries => common/src/main/java/net/momirealms/customcrops/common/plugin}/classpath/ClassPathAppender.java (96%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries => common/src/main/java/net/momirealms/customcrops/common/plugin}/classpath/ReflectionClassPathAppender.java (88%) rename {plugin/src/main/java/net/momirealms/customcrops/libraries => common/src/main/java/net/momirealms/customcrops/common/plugin}/classpath/URLClassLoaderAccess.java (95%) rename {api/src/main/java/net/momirealms/customcrops/api/common => common/src/main/java/net/momirealms/customcrops/common/plugin/feature}/Reloadable.java (76%) rename plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/JarInJarClassPathAppender.java => common/src/main/java/net/momirealms/customcrops/common/plugin/logging/JavaPluginLogger.java (54%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/plugin/logging/Log4jPluginLogger.java rename plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoadingException.java => common/src/main/java/net/momirealms/customcrops/common/plugin/logging/PluginLogger.java (71%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/plugin/logging/Slf4jPluginLogger.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/AbstractJavaScheduler.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/RegionExecutor.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/SchedulerAdapter.java rename plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoaderBootstrap.java => common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/SchedulerTask.java (84%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/sender/AbstractSender.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/sender/DummyConsoleSender.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/sender/Sender.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/sender/SenderFactory.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/ArrayUtils.java rename {plugin/src/main/java/net/momirealms/customcrops => common/src/main/java/net/momirealms/customcrops/common}/util/ClassUtils.java (50%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/CompletableFutures.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/Either.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/EitherImpl.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/FileUtils.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/Key.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/ListUtils.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/Pair.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/RandomUtils.java rename plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionResult.java => common/src/main/java/net/momirealms/customcrops/common/util/TriConsumer.java (78%) rename api/src/main/java/net/momirealms/customcrops/api/common/Pair.java => common/src/main/java/net/momirealms/customcrops/common/util/TriFunction.java (59%) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/Tristate.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/Tuple.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/UUIDUtils.java create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/WeightUtils.java create mode 100644 common/src/main/resources/custom-crops.properties create mode 100644 compatibility-asp-r1/build.gradle.kts create mode 100644 compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java rename {oraxen-legacy => compatibility-itemsadder-r1}/build.gradle.kts (59%) create mode 100644 compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java create mode 100644 compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderProvider.java rename {legacy-api => compatibility-oraxen-r1}/.gitignore (100%) create mode 100644 compatibility-oraxen-r1/build.gradle.kts create mode 100644 compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenListener.java create mode 100644 compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenProvider.java rename {oraxen-j21 => compatibility-oraxen-r2}/.gitignore (100%) rename {oraxen-j21 => compatibility-oraxen-r2}/build.gradle.kts (70%) create mode 100644 compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenListener.java create mode 100644 compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenProvider.java create mode 100644 compatibility/build.gradle.kts rename {plugin => compatibility}/libs/AdvancedSeasons-API.jar (100%) rename {plugin => compatibility}/libs/BattlePass-4.0.6-api.jar (100%) rename {plugin => compatibility}/libs/ClueScrolls-api.jar (100%) rename {plugin => compatibility}/libs/RealisticSeasons-api.jar (100%) rename {plugin => compatibility}/libs/mcMMO-api.jar (100%) rename {plugin => compatibility}/libs/zaphkiel-2.0.24.jar (100%) create mode 100644 compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/VaultHook.java create mode 100644 compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/CustomFishingItemProvider.java rename plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MMOItemsItemProvider.java (66%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MythicMobsItemProvider.java (67%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/item/NeigeItemsItemImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/NeigeItemsItemProvider.java (66%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/item/ZaphkielItemImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/ZaphkielItemProvider.java (61%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AuraSkillsImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/AuraSkillsLevelerProvider.java (69%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/AureliumSkillsProvider.java (52%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoJobsImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/EcoJobsLevelerProvider.java (69%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoSkillsImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/EcoSkillsLevelerProvider.java (67%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/level/JobsRebornImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/JobsRebornLevelerProvider.java (67%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/level/MMOCoreImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/MMOCoreLevelerProvider.java (68%) rename plugin/src/main/java/net/momirealms/customcrops/compatibility/level/McMMOImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/McMMOLevelerProvider.java (66%) create mode 100644 compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/papi/CustomCropsPapi.java rename plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java => compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/season/AdvancedSeasonsProvider.java (59%) create mode 100644 compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/season/RealisticSeasonsProvider.java delete mode 100644 legacy-api/build.gradle.kts delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/ItemType.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/crop/GrowingCrop.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Fertilizer.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/sprinkler/Sprinkler.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/ChunkCoordinate.java delete mode 100644 legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/SimpleLocation.java delete mode 100644 oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java delete mode 100644 oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java delete mode 100644 oraxen-legacy/.gitignore delete mode 100644 oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java delete mode 100644 oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java delete mode 100644 plugin/libs/Sparrow-Heart-0.19.jar delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java rename plugin/src/main/java/net/momirealms/customcrops/{mechanic/misc/value/ExpressionValue.java => bukkit/BukkitBootstrap.java} (55%) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandFeature.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugDataCommand.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ReloadCommand.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/BukkitIntegrationManager.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java rename plugin/src/main/java/net/momirealms/customcrops/{mechanic/item/factory => bukkit/item}/BukkitItemFactory.java (87%) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemManager.java rename plugin/src/main/java/net/momirealms/customcrops/{mechanic/item/factory/impl => bukkit/item}/ComponentItemFactory.java (92%) rename plugin/src/main/java/net/momirealms/customcrops/{mechanic/item/factory/impl => bukkit/item}/UniversalItemFactory.java (89%) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/BlockRequirementManager.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/BukkitSchedulerAdapter.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/DummyTask.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/impl/BukkitExecutor.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/impl/FoliaExecutor.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/sender/BukkitSenderFactory.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/world/BukkitWorldManager.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/VaultHook.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/ParseUtils.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BetonQuestHook.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/ClueScrollsHook.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/season/InBuiltSeason.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/compatibility/season/RealisticSeasonsImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/libraries/loader/JarInJarClassLoader.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/manager/PacketManager.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/AbstractEventItem.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ComponentKeys.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/Item.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/CFunction.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakWrapper.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/ConditionWrapper.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractBlockWrapper.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractFurnitureWrapper.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractWrapper.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceBlockWrapper.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceWrapper.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/AbstractFertilizer.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/SprinklerConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/QualityCropConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SoilRetainConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SpeedGrowConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/VariationConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/YieldIncreaseConfig.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/migrator/Migration.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CRegion.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CSection.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableChunk.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableSection.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/scheduler/BukkitSchedulerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/scheduler/SchedulerImpl.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/scheduler/SyncScheduler.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/scheduler/task/ReplaceTask.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java delete mode 100644 plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java create mode 100644 plugin/src/main/resources/commands.yml delete mode 100644 plugin/src/main/resources/messages/en.yml delete mode 100644 plugin/src/main/resources/messages/es.yml delete mode 100644 plugin/src/main/resources/messages/fr.yml delete mode 100644 plugin/src/main/resources/messages/pt.yml delete mode 100644 plugin/src/main/resources/messages/ru.yml delete mode 100644 plugin/src/main/resources/messages/zh_cn.yml create mode 100644 plugin/src/main/resources/translations/en.yml create mode 100644 plugin/src/main/resources/translations/zh_cn.yml create mode 100644 settings.gradle.kts diff --git a/CustomCrops_3.4_Basic_Pack.zip b/CustomCrops_Basic_Pack.zip similarity index 100% rename from CustomCrops_3.4_Basic_Pack.zip rename to CustomCrops_Basic_Pack.zip diff --git a/README.md b/README.md index 1d1e764cd..6696858df 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,38 @@ -# Note: This project is undergoing a major refactoring. Please switch to the 3.6-dev branch for the new API. - # Custom-Crops ![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/Xiao-MoMi/Custom-Crops) -![Code Size](https://img.shields.io/github/languages/code-size/Xiao-MoMi/Custom-Crops) -![bStats Servers](https://img.shields.io/bstats/servers/16593) -![bStats Players](https://img.shields.io/bstats/players/16593) -[![Scc Count Badge](https://sloc.xyz/github/Xiao-MoMi/Custom-Crops/?category=codes)](https://github.com/Xiao-MoMi/Custom-Crops/) -![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Crops) [![](https://jitpack.io/v/Xiao-MoMi/Custom-Crops.svg)](https://jitpack.io/#Xiao-MoMi/Custom-Crops) Gitbook +[![Scc Count Badge](https://sloc.xyz/github/Xiao-MoMi/Custom-Crops/?category=codes)](https://github.com/Xiao-MoMi/Custom-Crops/) +![Code Size](https://img.shields.io/github/languages/code-size/Xiao-MoMi/Custom-Crops) +![bStats Servers](https://img.shields.io/bstats/servers/16593) +![bStats Players](https://img.shields.io/bstats/players/16593) +![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Crops) -Ultra-customizable planting experience for Minecraft servers - -### Support the developer - -https://afdian.net/@xiaomomi - -https://polymart.org/resource/customcrops.2625 +CustomCrops is a Paper plugin crafted to deliver an exceptional planting experience for Minecraft servers, with a strong emphasis on customization and performance. It employs Zstd compression for data serialization, ensuring high efficiency comparable to Minecraft's own serialization techniques. The plugin optimizes server performance by running its tick system across multiple threads, reverting to the main thread only when required. Additionally, CustomCrops offers a comprehensive API that enables developers to create custom block mechanics with specific interaction and tick behaviors, such as a fish trap block that periodically provides players with fish. ## How to build -### Windows - #### Command Line -Install JDK 17 and set the JDK installation path to JAVA_HOME as an environment variable.\ -Start powershell and change directory to the project folder.\ -Execute ".\gradlew build" and get the jar at /target/CustomCrops-plugin-version.jar. +Install JDK 17 & 21. \ +Start terminal and change directory to the project folder.\ +Execute ".\gradlew build" and get the artifact under /target folder #### IDE -Import the project and execute gradle build action. +Import the project and execute gradle build action. \ +Get the artifact under /target folder -## Use CustomCrops API +## Support the developer + +Polymart: https://polymart.org/resource/customfishing.2723 \ +Afdian: https://afdian.net/@xiaomomi + +## CustomCrops API ### Maven -``` +```html jitpack @@ -44,7 +40,7 @@ Import the project and execute gradle build action. ``` -``` +```html com.github.Xiao-MoMi @@ -56,24 +52,24 @@ Import the project and execute gradle build action. ``` ### Gradle (Groovy) -``` +```groovy repositories { maven { url 'https://jitpack.io' } } ``` -``` +```groovy dependencies { compileOnly 'com.github.Xiao-MoMi:Custom-Crops:{LATEST}' } ``` ### Gradle (Kotlin) -``` +```kotlin repositories { maven("https://jitpack.io/") } ``` -``` +```kotlin dependencies { compileOnly("com.github.Xiao-MoMi:Custom-Crops:{LATEST}") } diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 4e9c7bd20..dff97c239 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,11 +1,28 @@ -dependencies { - compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") - implementation("com.flowpowered:flow-nbt:2.0.2") +plugins { + id("io.github.goooler.shadow") version "8.1.8" + id("maven-publish") } -tasks.withType { - options.encoding = "UTF-8" - options.release.set(17) +repositories { + mavenCentral() + maven("https://jitpack.io/") + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.rapture.pw/repository/maven-releases/") // flow nbt + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") +} + +dependencies { + implementation(project(":common")) + implementation("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + implementation("com.flowpowered:flow-nbt:${rootProject.properties["flow_nbt_version"]}") + implementation("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") { + exclude(module = "adventure-bom") + exclude(module = "checker-qual") + exclude(module = "annotations") + } + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") + compileOnly("com.github.Xiao-MoMi:Sparrow-Heart:${rootProject.properties["sparrow_heart_version"]}") } java { @@ -14,4 +31,30 @@ java { toolchain { languageVersion = JavaLanguageVersion.of(17) } -} \ No newline at end of file +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} + +tasks { + shadowJar { + archiveClassifier = "" + archiveFileName = "CustomCrops-${rootProject.properties["project_version"]}.jar" + relocate("net.kyori", "net.momirealms.customcrops.libraries") + relocate("dev.dejvokep", "net.momirealms.customcrops.libraries") + } +} + +publishing { + publications { + create("mavenJava") { + groupId = "net.momirealms" + artifactId = "CustomCrops" + version = rootProject.version.toString() + artifact(tasks.shadowJar) + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java new file mode 100644 index 000000000..cdf77e6c4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java @@ -0,0 +1,178 @@ +/* + * 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.action.ActionManager; +import net.momirealms.customcrops.api.core.AbstractItemManager; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.RegistryAccess; +import net.momirealms.customcrops.api.core.world.WorldManager; +import net.momirealms.customcrops.api.integration.IntegrationManager; +import net.momirealms.customcrops.api.misc.cooldown.CoolDownManager; +import net.momirealms.customcrops.api.misc.placeholder.PlaceholderManager; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.common.dependency.DependencyManager; +import net.momirealms.customcrops.common.locale.TranslationManager; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; +import net.momirealms.customcrops.common.plugin.scheduler.AbstractJavaScheduler; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerAdapter; +import net.momirealms.customcrops.common.sender.SenderFactory; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +public abstract class BukkitCustomCropsPlugin implements CustomCropsPlugin { + + private static BukkitCustomCropsPlugin instance; + private final Plugin boostrap; + + protected AbstractJavaScheduler scheduler; + protected DependencyManager dependencyManager; + protected TranslationManager translationManager; + protected AbstractItemManager itemManager; + protected PlaceholderManager placeholderManager; + protected CoolDownManager coolDownManager; + protected ConfigManager configManager; + protected IntegrationManager integrationManager; + protected WorldManager worldManager; + protected RegistryAccess registryAccess; + protected SenderFactory senderFactory; + + protected final Map, ActionManager> actionManagers = new HashMap<>(); + protected final Map, RequirementManager> requirementManagers = new HashMap<>(); + + /** + * Constructs a new BukkitCustomCropsPlugin instance. + * + * @param boostrap the plugin instance used to initialize this class + */ + public BukkitCustomCropsPlugin(Plugin boostrap) { + if (!boostrap.getName().equals("CustomCrops")) { + throw new IllegalArgumentException("CustomCrops plugin requires custom crops plugin"); + } + this.boostrap = boostrap; + instance = this; + } + + /** + * Retrieves the singleton instance of BukkitCustomFishingPlugin. + * + * @return the singleton instance + * @throws IllegalArgumentException if the plugin is not initialized + */ + public static BukkitCustomCropsPlugin getInstance() { + if (instance == null) { + throw new IllegalArgumentException("Plugin not initialized"); + } + return instance; + } + + /** + * Retrieves the plugin instance used to initialize this class. + * + * @return the {@link Plugin} instance + */ + public Plugin getBoostrap() { + return boostrap; + } + + /** + * Retrieves the DependencyManager. + * + * @return the {@link DependencyManager} + */ + @Override + public DependencyManager getDependencyManager() { + return dependencyManager; + } + + /** + * Retrieves the TranslationManager. + * + * @return the {@link TranslationManager} + */ + @Override + public TranslationManager getTranslationManager() { + return translationManager; + } + + public AbstractItemManager getItemManager() { + return itemManager; + } + + @Override + public SchedulerAdapter getScheduler() { + return scheduler; + } + + public SenderFactory getSenderFactory() { + return senderFactory; + } + + public WorldManager getWorldManager() { + return worldManager; + } + + public IntegrationManager getIntegrationManager() { + return integrationManager; + } + + public PlaceholderManager getPlaceholderManager() { + return placeholderManager; + } + + /** + * Logs a debug message. + * + * @param message the message to log + */ + public abstract void debug(Object message); + + public File getDataFolder() { + return boostrap.getDataFolder(); + } + + public RegistryAccess getRegistryAccess() { + return registryAccess; + } + + @SuppressWarnings("unchecked") + public ActionManager getActionManager(Class type) { + if (type == null) { + throw new IllegalArgumentException("Type cannot be null"); + } + return (ActionManager) actionManagers.get(type); + } + + @SuppressWarnings("unchecked") + public RequirementManager getRequirementManager(Class type) { + if (type == null) { + throw new IllegalArgumentException("Type cannot be null"); + } + return (RequirementManager) instance.requirementManagers.get(type); + } + + public CoolDownManager getCoolDownManager() { + return coolDownManager; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java deleted file mode 100644 index a24ef6e57..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager.*; -import net.momirealms.customcrops.api.scheduler.Scheduler; -import org.bukkit.plugin.java.JavaPlugin; - -public abstract class CustomCropsPlugin extends JavaPlugin { - - protected static CustomCropsPlugin instance; - protected VersionManager versionManager; - protected ConfigManager configManager; - protected Scheduler scheduler; - protected RequirementManager requirementManager; - protected ActionManager actionManager; - protected IntegrationManager integrationManager; - protected CoolDownManager coolDownManager; - protected WorldManager worldManager; - protected ItemManager itemManager; - protected AdventureManager adventure; - protected MessageManager messageManager; - protected ConditionManager conditionManager; - protected PlaceholderManager placeholderManager; - - public CustomCropsPlugin() { - instance = this; - } - - public static CustomCropsPlugin getInstance() { - return instance; - } - - public static CustomCropsPlugin get() { - return instance; - } - - /* Get version manager */ - public VersionManager getVersionManager() { - return versionManager; - } - - /* Get config manager */ - public ConfigManager getConfigManager() { - return configManager; - } - - /* Get scheduler */ - public Scheduler getScheduler() { - return scheduler; - } - - /* Get requirement manager */ - public RequirementManager getRequirementManager() { - return requirementManager; - } - - /* Get integration manager */ - public IntegrationManager getIntegrationManager() { - return integrationManager; - } - - /* Get action manager */ - public ActionManager getActionManager() { - return actionManager; - } - - /* Get cool down manager */ - public CoolDownManager getCoolDownManager() { - return coolDownManager; - } - - /* Get world data manager */ - public WorldManager getWorldManager() { - return worldManager; - } - - /* Get item manager */ - public ItemManager getItemManager() { - return itemManager; - } - - /* Get message manager */ - public MessageManager getMessageManager() { - return messageManager; - } - - /* Get adventure manager */ - public AdventureManager getAdventure() { - return adventure; - } - - /* Get condition manager */ - public ConditionManager getConditionManager() { - return conditionManager; - } - - /* Get placeholder manager */ - public PlaceholderManager getPlaceholderManager() { - return placeholderManager; - } - - public abstract boolean isHookedPluginEnabled(String plugin); - - public abstract void debug(String debug); - - public abstract void reload(); - - public abstract boolean isHookedPluginEnabled(String hooked, String... versionPrefix); - - public abstract boolean doesHookedPluginExist(String plugin); - - public abstract String getServerVersion(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java new file mode 100644 index 000000000..5edb7381b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java @@ -0,0 +1,891 @@ +package net.momirealms.customcrops.api.action; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.audience.Audience; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.block.*; +import net.momirealms.customcrops.api.core.item.Fertilizer; +import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.world.CustomCropsChunk; +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.event.CropPlantEvent; +import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.api.util.*; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.helper.VersionHelper; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customcrops.common.util.*; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.feature.entity.FakeEntity; +import net.momirealms.sparrow.heart.feature.entity.armorstand.FakeArmorStand; +import net.momirealms.sparrow.heart.feature.entity.display.FakeItemDisplay; +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import static java.util.Objects.requireNonNull; + +public abstract class AbstractActionManager implements ActionManager { + + protected final BukkitCustomCropsPlugin plugin; + private final HashMap> actionFactoryMap = new HashMap<>(); + private static final String EXPANSION_FOLDER = "expansions/action"; + + public AbstractActionManager(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + this.registerBuiltInActions(); + } + + protected void registerBuiltInActions() { + this.registerCommandAction(); + this.registerBroadcastAction(); + this.registerNearbyMessage(); + this.registerNearbyActionBar(); + this.registerNearbyTitle(); + this.registerParticleAction(); + this.registerQualityCropsAction(); + this.registerDropItemsAction(); + this.registerLegacyDropItemsAction(); + this.registerFakeItemAction(); + this.registerPlantAction(); + this.registerBreakAction(); + } + + @Override + public boolean registerAction(ActionFactory actionFactory, String... types) { + for (String type : types) { + if (this.actionFactoryMap.containsKey(type)) return false; + } + for (String type : types) { + this.actionFactoryMap.put(type, actionFactory); + } + return true; + } + + @Override + public boolean unregisterAction(String type) { + return this.actionFactoryMap.remove(type) != null; + } + + @Override + public boolean hasAction(@NotNull String type) { + return actionFactoryMap.containsKey(type); + } + + @Nullable + @Override + public ActionFactory getActionFactory(@NotNull String type) { + return actionFactoryMap.get(type); + } + + @Override + public Action parseAction(Section section) { + if (section == null) return Action.empty(); + ActionFactory factory = getActionFactory(section.getString("type")); + if (factory == null) { + plugin.getPluginLogger().warn("Action type: " + section.getString("type") + " doesn't exist."); + return Action.empty(); + } + return factory.process(section.get("value"), section.getDouble("chance", 1d)); + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public Action[] parseActions(Section section) { + ArrayList> actionList = new ArrayList<>(); + if (section != null) + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + Action action = parseAction(innerSection); + if (action != null) + actionList.add(action); + } + } + return actionList.toArray(new Action[0]); + } + + @Override + public Action parseAction(@NotNull String type, @NotNull Object args) { + ActionFactory factory = getActionFactory(type); + if (factory == null) { + plugin.getPluginLogger().warn("Action type: " + type + " doesn't exist."); + return Action.empty(); + } + return factory.process(args, 1); + } + + @SuppressWarnings({"ResultOfMethodCallIgnored", "unchecked"}) + protected void loadExpansions(Class tClass) { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + + List>> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class> expansionClass = (Class>) ClassUtils.findClass(expansionJar, ActionExpansion.class, tClass); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + plugin.getPluginLogger().warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class> expansionClass : classes) { + ActionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterAction(expansion.getActionType()); + registerAction(expansion.getActionFactory(), expansion.getActionType()); + plugin.getPluginLogger().info("Loaded action expansion: " + expansion.getActionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + plugin.getPluginLogger().warn("Error occurred when creating expansion instance.", e); + } + } + + protected void registerBroadcastAction() { + registerAction((args, chance) -> { + List messages = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + OfflinePlayer offlinePlayer = null; + if (context.holder() instanceof Player player) { + offlinePlayer = player; + } + List replaced = plugin.getPlaceholderManager().parse(offlinePlayer, messages, context.placeholderMap()); + for (Player player : Bukkit.getOnlinePlayers()) { + Audience audience = plugin.getSenderFactory().getAudience(player); + for (String text : replaced) { + audience.sendMessage(AdventureHelper.miniMessage(text)); + } + } + }; + }, "broadcast"); + } + + protected void registerNearbyMessage() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + List messages = ListUtils.toList(section.get("message")); + MathValue range = MathValue.auto(section.get("range")); + return context -> { + if (Math.random() > chance) return; + double realRange = range.evaluate(context); + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + List replaced = BukkitPlaceholderManager.getInstance().parse( + owner, + messages, + context.placeholderMap() + ); + Audience audience = plugin.getSenderFactory().getAudience(player); + for (String text : replaced) { + audience.sendMessage(AdventureHelper.miniMessage(text)); + } + } + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at message-nearby action which should be Section"); + return Action.empty(); + } + }, "message-nearby"); + } + + protected void registerNearbyActionBar() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + String actionbar = section.getString("actionbar"); + MathValue range = MathValue.auto(section.get("range")); + return context -> { + if (Math.random() > chance) return; + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + double realRange = range.evaluate(context); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + String replaced = plugin.getPlaceholderManager().parse(owner, actionbar, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(player); + audience.sendActionBar(AdventureHelper.miniMessage(replaced)); + } + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at actionbar-nearby action which should be Section"); + return Action.empty(); + } + }, "actionbar-nearby"); + } + + protected void registerCommandAction() { + registerAction((args, chance) -> { + List commands = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + List replaced = BukkitPlaceholderManager.getInstance().parse(owner, commands, context.placeholderMap()); + plugin.getScheduler().sync().run(() -> { + for (String text : replaced) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); + } + }, null); + }; + }, "command"); + registerAction((args, chance) -> { + List commands = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + String random = commands.get(ThreadLocalRandom.current().nextInt(commands.size())); + random = BukkitPlaceholderManager.getInstance().parse(owner, random, context.placeholderMap()); + String finalRandom = random; + plugin.getScheduler().sync().run(() -> { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom); + }, null); + }; + }, "random-command"); + registerAction((args, chance) -> { + if (args instanceof Section section) { + List cmd = ListUtils.toList(section.get("command")); + MathValue range = MathValue.auto(section.get("range")); + return context -> { + if (Math.random() > chance) return; + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + double realRange = range.evaluate(context); + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + List replaced = BukkitPlaceholderManager.getInstance().parse(owner, cmd, context.placeholderMap()); + for (String text : replaced) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); + } + } + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at command-nearby action which should be Section"); + return Action.empty(); + } + }, "command-nearby"); + } + + protected void registerBundleAction(Class tClass) { + registerAction((args, chance) -> { + List> actions = new ArrayList<>(); + if (args instanceof Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + actions.add(parseAction(innerSection)); + } + } + } + return context -> { + if (Math.random() > chance) return; + for (Action action : actions) { + action.trigger(context); + } + }; + }, "chain"); + registerAction((args, chance) -> { + List> actions = new ArrayList<>(); + int delay; + boolean async; + if (args instanceof Section section) { + delay = section.getInt("delay", 1); + async = section.getBoolean("async", false); + Section actionSection = section.getSection("actions"); + if (actionSection != null) + for (Map.Entry entry : actionSection.getStringRouteMappedValues(false).entrySet()) + if (entry.getValue() instanceof Section innerSection) + actions.add(parseAction(innerSection)); + } else { + delay = 1; + async = false; + } + return context -> { + if (Math.random() > chance) return; + Location location = context.arg(ContextKeys.LOCATION); + if (async) { + plugin.getScheduler().asyncLater(() -> { + for (Action action : actions) + action.trigger(context); + }, delay * 50L, TimeUnit.MILLISECONDS); + } else { + plugin.getScheduler().sync().runLater(() -> { + for (Action action : actions) + action.trigger(context); + }, delay, location); + } + }; + }, "delay"); + registerAction((args, chance) -> { + List> actions = new ArrayList<>(); + int delay, duration, period; + boolean async; + if (args instanceof Section section) { + delay = section.getInt("delay", 2); + duration = section.getInt("duration", 20); + period = section.getInt("period", 2); + async = section.getBoolean("async", false); + Section actionSection = section.getSection("actions"); + if (actionSection != null) + for (Map.Entry entry : actionSection.getStringRouteMappedValues(false).entrySet()) + if (entry.getValue() instanceof Section innerSection) + actions.add(parseAction(innerSection)); + } else { + delay = 1; + period = 1; + async = false; + duration = 20; + } + return context -> { + if (Math.random() > chance) return; + Location location = context.arg(ContextKeys.LOCATION); + SchedulerTask task; + if (async) { + task = plugin.getScheduler().asyncRepeating(() -> { + for (Action action : actions) { + action.trigger(context); + } + }, delay * 50L, period * 50L, TimeUnit.MILLISECONDS); + } else { + task = plugin.getScheduler().sync().runRepeating(() -> { + for (Action action : actions) { + action.trigger(context); + } + }, delay, period, location); + } + plugin.getScheduler().asyncLater(task::cancel, duration * 50L, TimeUnit.MILLISECONDS); + }; + }, "timer"); + registerAction((args, chance) -> { + if (args instanceof Section section) { + Action[] actions = parseActions(section.getSection("actions")); + Requirement[] requirements = plugin.getRequirementManager(tClass).parseRequirements(section.getSection("conditions"), true); + return condition -> { + if (Math.random() > chance) return; + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied(condition)) { + return; + } + } + for (Action action : actions) { + action.trigger(condition); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at conditional action which is expected to be `Section`"); + return Action.empty(); + } + }, "conditional"); + registerAction((args, chance) -> { + if (args instanceof Section section) { + List[], Action[]>> conditionActionPairList = new ArrayList<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + Action[] actions = parseActions(inner.getSection("actions")); + Requirement[] requirements = plugin.getRequirementManager(tClass).parseRequirements(inner.getSection("conditions"), false); + conditionActionPairList.add(Pair.of(requirements, actions)); + } + } + return context -> { + if (Math.random() > chance) return; + outer: + for (Pair[], Action[]> pair : conditionActionPairList) { + if (pair.left() != null) + for (Requirement requirement : pair.left()) { + if (!requirement.isSatisfied(context)) { + continue outer; + } + } + if (pair.right() != null) + for (Action action : pair.right()) { + action.trigger(context); + } + return; + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at priority action which is expected to be `Section`"); + return Action.empty(); + } + }, "priority"); + } + + protected void registerNearbyTitle() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + TextValue title = TextValue.auto(section.getString("title")); + TextValue subtitle = TextValue.auto(section.getString("subtitle")); + int fadeIn = section.getInt("fade-in", 20); + int stay = section.getInt("stay", 30); + int fadeOut = section.getInt("fade-out", 10); + int range = section.getInt("range", 0); + return context -> { + if (Math.random() > chance) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= range) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.sendTitle(audience, + AdventureHelper.miniMessage(title.render(context)), + AdventureHelper.miniMessage(subtitle.render(context)), + fadeIn, stay, fadeOut + ); + } + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at title-nearby action which is expected to be `Section`"); + return Action.empty(); + } + }, "title-nearby"); + } + + protected void registerParticleAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + Particle particleType = ParticleUtils.getParticle(section.getString("particle", "ASH").toUpperCase(Locale.ENGLISH)); + double x = section.getDouble("x",0.0); + double y = section.getDouble("y",0.0); + double z = section.getDouble("z",0.0); + double offSetX = section.getDouble("offset-x",0.0); + double offSetY = section.getDouble("offset-y",0.0); + double offSetZ = section.getDouble("offset-z",0.0); + int count = section.getInt("count", 1); + double extra = section.getDouble("extra", 0.0); + float scale = section.getDouble("scale", 1d).floatValue(); + + ItemStack itemStack; + if (section.contains("itemStack")) + itemStack = BukkitCustomCropsPlugin.getInstance() + .getItemManager() + .build(null, section.getString("itemStack")); + else + itemStack = null; + + Color color; + if (section.contains("color")) { + String[] rgb = section.getString("color","255,255,255").split(","); + color = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); + } else { + color = null; + } + + Color toColor; + if (section.contains("color")) { + String[] rgb = section.getString("to-color","255,255,255").split(","); + toColor = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); + } else { + toColor = null; + } + + return context -> { + if (Math.random() > chance) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + location.getWorld().spawnParticle( + particleType, + location.getX() + x, location.getY() + y, location.getZ() + z, + count, + offSetX, offSetY, offSetZ, + extra, + itemStack != null ? itemStack : (color != null && toColor != null ? new Particle.DustTransition(color, toColor, scale) : (color != null ? new Particle.DustOptions(color, scale) : null)) + ); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at particle action which is expected to be `Section`"); + return Action.empty(); + } + }, "particle"); + } + + protected void registerQualityCropsAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + MathValue min = MathValue.auto(section.get("min")); + MathValue max = MathValue.auto(section.get("max")); + boolean toInv = section.getBoolean("to-inventory", false); + String[] qualityLoots = new String[ConfigManager.defaultQualityRatio().length]; + for (int i = 1; i <= ConfigManager.defaultQualityRatio().length; i++) { + qualityLoots[i-1] = section.getString("items." + i); + if (qualityLoots[i-1] == null) { + plugin.getPluginLogger().warn("items." + i + " should not be null"); + qualityLoots[i-1] = ""; + } + } + return context -> { + if (Math.random() > chance) return; + double[] ratio = ConfigManager.defaultQualityRatio(); + int random = RandomUtils.generateRandomInt((int) min.evaluate(context), (int) max.evaluate(context)); + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isEmpty()) { + return; + } + Pos3 pos3 = Pos3.from(location); + Fertilizer[] fertilizers = null; + Player player = null; + if (context.holder() instanceof Player p) { + player = p; + } + Pos3 potLocation = pos3.add(0, -1, 0); + Optional chunk = world.get().getChunk(potLocation.toChunkPos()); + if (chunk.isPresent()) { + Optional state = chunk.get().getBlockState(potLocation); + if (state.isPresent()) { + if (state.get().type() instanceof PotBlock potBlock) { + fertilizers = potBlock.fertilizers(state.get()); + } + } + } + ArrayList configs = new ArrayList<>(); + if (fertilizers != null) { + for (Fertilizer fertilizer : fertilizers) { + Optional.ofNullable(fertilizer.config()).ifPresent(configs::add); + } + } + for (FertilizerConfig config : configs) { + random = config.processDroppedItemAmount(random); + double[] newRatio = config.overrideQualityRatio(); + if (newRatio != null) { + ratio = newRatio; + } + } + for (int i = 0; i < random; i++) { + double r1 = Math.random(); + for (int j = 0; j < ratio.length; j++) { + if (r1 < ratio[j]) { + ItemStack drop = plugin.getItemManager().build(player, qualityLoots[j]); + if (drop == null || drop.getType() == Material.AIR) return; + if (toInv && player != null) { + PlayerUtils.giveItem(player, drop, 1); + } else { + location.getWorld().dropItemNaturally(location, drop); + } + break; + } + } + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at quality-crops action which is expected to be `Section`"); + return Action.empty(); + } + }, "quality-crops"); + } + + protected void registerDropItemsAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + boolean ignoreFertilizer = section.getBoolean("ignore-fertilizer", true); + String item = section.getString("item"); + MathValue min = MathValue.auto(section.get("min")); + MathValue max = MathValue.auto(section.get("max")); + boolean toInv = section.getBoolean("to-inventory", false); + return context -> { + if (Math.random() > chance) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isEmpty()) { + return; + } + Player player = null; + if (context.holder() instanceof Player p) { + player = p; + } + ItemStack itemStack = plugin.getItemManager().build(player, item); + if (itemStack != null) { + int random = RandomUtils.generateRandomInt((int) min.evaluate(context), (int) max.evaluate(context)); + if (!ignoreFertilizer) { + Pos3 pos3 = Pos3.from(location); + Fertilizer[] fertilizers = null; + Pos3 potLocation = pos3.add(0, -1, 0); + Optional chunk = world.get().getChunk(potLocation.toChunkPos()); + if (chunk.isPresent()) { + Optional state = chunk.get().getBlockState(potLocation); + if (state.isPresent()) { + if (state.get().type() instanceof PotBlock potBlock) { + fertilizers = potBlock.fertilizers(state.get()); + } + } + } + ArrayList configs = new ArrayList<>(); + if (fertilizers != null) { + for (Fertilizer fertilizer : fertilizers) { + Optional.ofNullable(fertilizer.config()).ifPresent(configs::add); + } + } + for (FertilizerConfig config : configs) { + random = config.processDroppedItemAmount(random); + } + } + itemStack.setAmount(random); + if (toInv && player != null) { + PlayerUtils.giveItem(player, itemStack, random); + } else { + location.getWorld().dropItemNaturally(location, itemStack); + } + } else { + plugin.getPluginLogger().warn("Item: " + item + " doesn't exist"); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at drop-item action which is expected to be `Section`"); + return Action.empty(); + } + }, "drop-item"); + } + + protected void registerLegacyDropItemsAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + List> actions = new ArrayList<>(); + Section otherItemSection = section.getSection("other-items"); + if (otherItemSection != null) { + for (Map.Entry entry : otherItemSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + actions.add(requireNonNull(getActionFactory("drop-item")).process(inner, inner.getDouble("chance", 1D))); + } + } + } + Section qualitySection = section.getSection("quality-crops"); + if (qualitySection != null) { + actions.add(requireNonNull(getActionFactory("quality-crops")).process(qualitySection, 1)); + } + return context -> { + if (Math.random() > chance) return; + for (Action action : actions) { + action.trigger(context); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at drop-items action which is expected to be `Section`"); + return Action.empty(); + } + }, "drop-items"); + } + + private void registerFakeItemAction() { + registerAction(((args, chance) -> { + if (args instanceof Section section) { + String itemID = section.getString("item", ""); + String[] split = itemID.split(":"); + if (split.length >= 2) itemID = split[split.length - 1]; + MathValue duration = MathValue.auto(section.get("duration", 20)); + boolean position = !section.getString("position", "player").equals("player"); + MathValue x = MathValue.auto(section.get("x", 0)); + MathValue y = MathValue.auto(section.get("y", 0)); + MathValue z = MathValue.auto(section.get("z", 0)); + MathValue yaw = MathValue.auto(section.get("yaw", 0)); + int range = section.getInt("range", 0); + boolean useItemDisplay = section.getBoolean("use-item-display", false); + boolean onlyShowToOne = !section.getBoolean("visible-to-all", true); + String finalItemID = itemID; + return context -> { + if (Math.random() > chance) return; + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + Player owner = null; + if (context.holder() instanceof Player p) { + owner = p; + } + Location location = position ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : requireNonNull(owner).getLocation().clone(); + location.add(x.evaluate(context), y.evaluate(context) - 1, z.evaluate(context)); + location.setPitch(0); + location.setYaw((float) yaw.evaluate(context)); + FakeEntity fakeEntity; + if (useItemDisplay && VersionHelper.isVersionNewerThan1_19_4()) { + location.add(0,1.5,0); + FakeItemDisplay itemDisplay = SparrowHeart.getInstance().createFakeItemDisplay(location); + itemDisplay.item(plugin.getItemManager().build(owner, finalItemID)); + fakeEntity = itemDisplay; + } else { + FakeArmorStand armorStand = SparrowHeart.getInstance().createFakeArmorStand(location); + armorStand.invisible(true); + armorStand.equipment(EquipmentSlot.HEAD, plugin.getItemManager().build(owner, finalItemID)); + fakeEntity = armorStand; + } + ArrayList viewers = new ArrayList<>(); + if (onlyShowToOne) { + viewers.add(owner); + } else { + if (range > 0) { + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= range) { + viewers.add(player); + } + } + } else { + viewers.add(owner); + } + } + for (Player player : viewers) { + fakeEntity.spawn(player); + } + plugin.getScheduler().asyncLater(() -> { + for (Player player : viewers) { + if (player.isOnline() && player.isValid()) { + fakeEntity.destroy(player); + } + } + }, (long) (duration.evaluate(context) * 50), TimeUnit.MILLISECONDS); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at fake-item action which is expected to be `Section`"); + return Action.empty(); + } + }), "fake-item"); + } + + @SuppressWarnings("unchecked") + protected void registerPlantAction() { + this.registerAction((args, chance) -> { + if (args instanceof Section section) { + int point = section.getInt("point", 0); + String key = requireNonNull(section.getString("crop")); + int y = section.getInt("y", 0); + boolean triggerAction = section.getBoolean("trigger-event", false); + return context -> { + if (Math.random() > chance) return; + CropConfig cropConfig = Registries.CROP.get(key); + if (cropConfig == null) { + plugin.getPluginLogger().warn("`plant` action is not executed due to crop[" + key + "] not exists"); + return; + } + Location cropLocation = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0,y,0); + Location potLocation = cropLocation.clone().subtract(0,1,0); + Optional> optionalWorld = plugin.getWorldManager().getWorld(cropLocation.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + CustomCropsWorld world = optionalWorld.get(); + PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); + Pos3 potPos3 = Pos3.from(potLocation); + String potItemID = plugin.getItemManager().blockID(potLocation); + PotConfig potConfig = Registries.ITEM_TO_POT.get(potItemID); + CustomCropsBlockState potState = potBlock.fixOrGetState(world, potPos3, potConfig, potItemID); + if (potState == null) { + plugin.getPluginLogger().warn("Pot doesn't exist below the crop when executing `plant` action at location[" + world.worldName() + "," + potPos3 + "]"); + return; + } + + CropBlock cropBlock = (CropBlock) BuiltInBlockMechanics.CROP.mechanic(); + CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState(); + cropBlock.id(state, key); + cropBlock.point(state, point); + + if (context.holder() instanceof Player player) { + EquipmentSlot slot = requireNonNull(context.arg(ContextKeys.SLOT)); + CropPlantEvent plantEvent = new CropPlantEvent(player, player.getInventory().getItem(slot), slot, cropLocation, cropConfig, state, point); + if (EventUtils.fireAndCheckCancel(plantEvent)) { + return; + } + cropBlock.point(state, plantEvent.getPoint()); + if (triggerAction) { + ActionManager.trigger((Context) context, cropConfig.plantActions()); + } + } + + CropStageConfig stageConfigWithModel = cropConfig.stageWithModelByPoint(cropBlock.point(state)); + world.addBlockState(Pos3.from(cropLocation), state); + plugin.getScheduler().sync().run(() -> { + plugin.getItemManager().remove(cropLocation, ExistenceForm.ANY); + plugin.getItemManager().place(cropLocation, stageConfigWithModel.existenceForm(), requireNonNull(stageConfigWithModel.stageID()), cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE); + }, cropLocation); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plant action which is expected to be `Section`"); + return Action.empty(); + } + }, "plant", "replant"); + } + + protected void registerBreakAction() { + this.registerAction((args, chance) -> { + boolean triggerEvent = (boolean) args; + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + 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; + } + if (triggerEvent) { + CropStageConfig stageConfig = config.stageWithModelByPoint(cropBlock.point(state)); + Player player = null; + if (context.holder() instanceof Player p) { + player = p; + } + FakeCancellable fakeCancellable = new FakeCancellable(); + if (player != null) { + EquipmentSlot slot = requireNonNull(context.arg(ContextKeys.SLOT)); + ItemStack itemStack = player.getInventory().getItem(slot); + state.type().onBreak(new WrappedBreakEvent(player, null, world, location, stageConfig.stageID(), itemStack, plugin.getItemManager().id(itemStack), BreakReason.ACTION, fakeCancellable)); + } else { + state.type().onBreak(new WrappedBreakEvent(null, null, world, location, stageConfig.stageID(), null, null, BreakReason.ACTION, fakeCancellable)); + } + if (fakeCancellable.isCancelled()) { + return; + } + } + world.removeBlockState(pos3); + plugin.getItemManager().remove(location, ExistenceForm.ANY); + }; + }, "break"); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java b/api/src/main/java/net/momirealms/customcrops/api/action/Action.java similarity index 53% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java rename to api/src/main/java/net/momirealms/customcrops/api/action/Action.java index b11e32b09..a63636164 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/Action.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,16 +15,25 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.item.fertilizer; +package net.momirealms.customcrops.api.action; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.context.Context; -public interface Variation extends Fertilizer { +/** + * The Action interface defines a generic action that can be triggered based on a provided context. + * + * @param the type of the object that is used in the context for triggering the action. + */ +public interface Action { /** - * Get the bonus of variation chance + * Triggers the action based on the provided condition. * - * @return chance bonus + * @param context the context */ - double getChanceBonus(); + void trigger(Context context); + + static Action empty() { + return EmptyAction.instance(); + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/ActionExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/action/ActionExpansion.java new file mode 100644 index 000000000..d2dc3d334 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/ActionExpansion.java @@ -0,0 +1,55 @@ +/* + * 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.action; + +/** + * Abstract class representing an expansion of an action. + * This class should be extended to provide specific implementations of actions. + * + * @param the type parameter for the action factory + */ +public abstract class ActionExpansion { + + /** + * Retrieves the version of this action expansion. + * + * @return a String representing the version of the action expansion + */ + public abstract String getVersion(); + + /** + * Retrieves the author of this action expansion. + * + * @return a String representing the author of the action expansion + */ + public abstract String getAuthor(); + + /** + * Retrieves the type of this action. + * + * @return a String representing the type of action + */ + public abstract String getActionType(); + + /** + * Retrieves the action factory associated with this action expansion. + * + * @return an ActionFactory of type T that creates instances of the action + */ + public abstract ActionFactory getActionFactory(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java b/api/src/main/java/net/momirealms/customcrops/api/action/ActionFactory.java similarity index 58% rename from api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java rename to api/src/main/java/net/momirealms/customcrops/api/action/ActionFactory.java index c6a3ad053..61059b03d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/ActionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,18 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.common.item; +package net.momirealms.customcrops.api.action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.requirement.State; - -public interface EventItem { +/** + * Interface representing a factory for creating actions. + * + * @param the type of object that the action will operate on + */ +public interface ActionFactory { /** - * Trigger events + * Constructs an action based on the provided arguments. * - * @param actionTrigger trigger - * @param state state + * @param args the args containing the arguments needed to build the action + * @return the constructed action */ - void trigger(ActionTrigger actionTrigger, State state); + Action process(Object args, double chance); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/ActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/ActionManager.java new file mode 100644 index 000000000..082c711a0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/ActionManager.java @@ -0,0 +1,159 @@ +/* + * 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.action; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * The ActionManager interface manages custom action types and provides methods for handling actions. + * + * @param the type of the context in which the actions are triggered. + */ +public interface ActionManager extends Reloadable { + + /** + * Registers a custom action type with its corresponding factory. + * + * @param actionFactory The factory responsible for creating instances of the action. + * @param alias The type identifier of the action. + * @return True if registration was successful, false if the type is already registered. + */ + boolean registerAction(ActionFactory actionFactory, String... alias); + + /** + * Unregisters a custom action type. + * + * @param type The type identifier of the action to unregister. + * @return True if unregistration was successful, false if the type is not registered. + */ + boolean unregisterAction(String type); + + /** + * Checks if an action type is registered. + * + * @param type The type identifier of the action. + * @return True if the action type is registered, otherwise false. + */ + boolean hasAction(@NotNull String type); + + /** + * Retrieves the action factory for the specified action type. + * + * @param type The type identifier of the action. + * @return The action factory for the specified type, or null if no factory is found. + */ + @Nullable + ActionFactory getActionFactory(@NotNull String type); + + /** + * Parses an action from a configuration section. + * + * @param section The configuration section containing the action definition. + * @return The parsed action. + */ + Action parseAction(Section section); + + /** + * Parses an array of actions from a configuration section. + * + * @param section The configuration section containing the action definitions. + * @return An array of parsed actions. + */ + @NotNull + Action[] parseActions(Section section); + + /** + * Parses an action from the given type and arguments. + * + * @param type The type identifier of the action. + * @param args The arguments for the action. + * @return The parsed action. + */ + Action parseAction(@NotNull String type, @NotNull Object args); + + /** + * Generates a map of actions triggered by specific events from a configuration section. + * + * @param section The configuration section containing event-action mappings. + * @return A map where the keys are action triggers and the values are arrays of actions associated with those triggers. + */ + default Map[]> parseEventActions(Section section) { + HashMap[]> actionMap = new HashMap<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + try { + actionMap.put( + ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)), + parseActions(innerSection) + ); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } + } + } + return actionMap; + } + + /** + * Parses a configuration section to generate a map of timed actions. + * + * @param section The configuration section containing time-action mappings. + * @return A TreeMap where the keys are time values (in integer form) and the values are arrays of actions associated with those times. + */ + default TreeMap[]> parseTimesActions(Section section) { + TreeMap[]> actionMap = new TreeMap<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + actionMap.put(Integer.parseInt(entry.getKey()), parseActions(innerSection)); + } + } + return actionMap; + } + + /** + * Triggers a list of actions with the given context. + * If the list of actions is not null, each action in the list is triggered. + * + * @param context The context associated with the actions. + * @param actions The list of actions to trigger. + */ + static void trigger(@NotNull Context context, @Nullable List> actions) { + if (actions != null) + for (Action action : actions) + action.trigger(context); + } + + /** + * Triggers an array of actions with the given context. + * If the array of actions is not null, each action in the array is triggered. + * + * @param context The context associated with the actions. + * @param actions The array of actions to trigger. + */ + static void trigger(@NotNull Context context, @Nullable Action[] actions) { + if (actions != null) + for (Action action : actions) + action.trigger(context); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionTrigger.java b/api/src/main/java/net/momirealms/customcrops/api/action/ActionTrigger.java similarity index 79% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionTrigger.java rename to api/src/main/java/net/momirealms/customcrops/api/action/ActionTrigger.java index 909b7d9fb..caa7dd040 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionTrigger.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/ActionTrigger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,12 +15,10 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.item.function; +package net.momirealms.customcrops.api.action; -public enum FunctionTrigger { +public enum ActionTrigger { PLACE, BREAK, - BE_INTERACTED, - INTERACT_AT, - INTERACT_AIR + WORK, INTERACT } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java b/api/src/main/java/net/momirealms/customcrops/api/action/EmptyAction.java similarity index 58% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java rename to api/src/main/java/net/momirealms/customcrops/api/action/EmptyAction.java index f0460629d..3585f656a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/EmptyAction.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,17 +15,21 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.condition; +package net.momirealms.customcrops.api.action; -import net.momirealms.customcrops.api.mechanic.condition.Condition; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.context.Context; -public class EmptyCondition implements Condition { +/** + * An implementation of the Action interface that represents an empty action with no behavior. + * This class serves as a default action to prevent NPE. + */ +public class EmptyAction implements Action { - public static EmptyCondition instance = new EmptyCondition(); + public static EmptyAction instance() { + return new EmptyAction<>(); + } @Override - public boolean isConditionMet(CustomCropsBlock block, boolean offline) { - return true; + public void trigger(Context context) { } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/Tuple.java b/api/src/main/java/net/momirealms/customcrops/api/common/Tuple.java deleted file mode 100644 index f7789a0d9..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/common/Tuple.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.common; - -public class Tuple { - - private L left; - private M mid; - private R right; - - public Tuple(L left, M mid, R right) { - this.left = left; - this.mid = mid; - this.right = right; - } - - public static Tuple of(final L left, final M mid, final R right) { - return new Tuple<>(left, mid, right); - } - - public L getLeft() { - return left; - } - - public void setLeft(L left) { - this.left = left; - } - - public M getMid() { - return mid; - } - - public void setMid(M mid) { - this.mid = mid; - } - - public R getRight() { - return right; - } - - public void setRight(R right) { - this.right = right; - } -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java b/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java deleted file mode 100644 index 92d06af23..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.common.item; - -public interface KeyItem { - - /** - * Get item's key - * - * @return key - */ - String getKey(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/AbstractContext.java b/api/src/main/java/net/momirealms/customcrops/api/context/AbstractContext.java new file mode 100644 index 000000000..effa86cca --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/context/AbstractContext.java @@ -0,0 +1,64 @@ +package net.momirealms.customcrops.api.context; + +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class AbstractContext implements Context { + + private final T holder; + private final Map, Object> args; + private final Map placeholderMap; + + public AbstractContext(@Nullable T holder, boolean sync) { + this.holder = holder; + this.args = sync ? new ConcurrentHashMap<>() : new HashMap<>(); + this.placeholderMap = sync ? new ConcurrentHashMap<>() : new HashMap<>(); + } + + @Override + public Map, Object> args() { + return args; + } + + @Override + public Map placeholderMap() { + return placeholderMap; + } + + @Override + public AbstractContext arg(ContextKeys key, C value) { + if (key == null || value == null) return this; + this.args.put(key, value); + this.placeholderMap.put("{" + key.key() + "}", value.toString()); + return this; + } + + @Override + public AbstractContext combine(Context other) { + this.args.putAll(other.args()); + this.placeholderMap.putAll(other.placeholderMap()); + return this; + } + + @Override + @SuppressWarnings("unchecked") + public C arg(ContextKeys key) { + return (C) args.get(key); + } + + @Nullable + @SuppressWarnings("unchecked") + @Override + public C remove(ContextKeys key) { + placeholderMap.remove("{" + key.key() + "}"); + return (C) args.remove(key); + } + + @Override + public T holder() { + return holder; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java b/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java new file mode 100644 index 000000000..c39cf7e56 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java @@ -0,0 +1,19 @@ +package net.momirealms.customcrops.api.context; + +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import org.jetbrains.annotations.NotNull; + +public class BlockContextImpl extends AbstractContext { + + public BlockContextImpl(@NotNull CustomCropsBlockState block, boolean sync) { + super(block, sync); + } + + @Override + public String toString() { + return "BlockContext{" + + "args=" + args() + + ", block=" + holder() + + '}'; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/Context.java b/api/src/main/java/net/momirealms/customcrops/api/context/Context.java new file mode 100644 index 000000000..1cb4999d2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/context/Context.java @@ -0,0 +1,152 @@ +/* + * 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.context; + +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +/** + * The Context interface represents a generic context for custom crops mechanics. + * It allows for storing and retrieving arguments, as well as getting the holder + * of the context. This can be used to maintain state or pass parameters within + * the custom crops mechanics. + * + * @param the type of the holder object for this context + */ +public interface Context { + + /** + * Retrieves the map of arguments associated with this context. + * + * @return a map where the keys are argument names and the values are argument values. + */ + Map, Object> args(); + + /** + * Converts the context to a map of placeholders + * + * @return a map of placeholders + */ + Map placeholderMap(); + + /** + * Adds or updates an argument in the context. + * This method allows adding a new argument or updating the value of an existing argument. + * + * @param the type of the value being added to the context. + * @param key the ContextKeys key representing the argument to be added or updated. + * @param value the value to be associated with the specified key. + * @return the current context instance, allowing for method chaining. + */ + Context arg(ContextKeys key, C value); + + /** + * Combines one context with another + * + * @param other other + * @return this context + */ + Context combine(Context other); + + /** + * Retrieves the value of a specific argument from the context. + * This method fetches the value associated with the specified ContextKeys key. + * + * @param the type of the value being retrieved. + * @param key the ContextKeys key representing the argument to be retrieved. + * @return the value associated with the specified key, or null if the key does not exist. + */ + @Nullable + C arg(ContextKeys key); + + /** + * Retrieves the value of a specific argument from the context. + * This method fetches the value associated with the specified ContextKeys key. + * + * @param the type of the value being retrieved. + * @param key the ContextKeys key representing the argument to be retrieved. + * @return the value associated with the specified key, or null if the key does not exist. + */ + default C argOrDefault(ContextKeys key, C value) { + C result = arg(key); + return result == null ? value : result; + } + + /** + * Remove the key from the context + * + * @param key the ContextKeys key + * @return the removed value + * @param the type of the value being removed. + */ + @Nullable + C remove(ContextKeys key); + + /** + * Gets the holder of this context. + * + * @return the holder object of type T. + */ + T holder(); + + /** + * Creates a player-specific context. + * + * @param player the player to be used as the holder of the context. + * @return a new Context instance with the specified player as the holder. + */ + static Context player(@Nullable Player player) { + return new PlayerContextImpl(player, false); + } + + /** + * Creates a block-specific context. + * + * @param block the block to be used as the holder of the context. + * @return a new Context instance with the specified block as the holder. + */ + static Context block(@NotNull CustomCropsBlockState block) { + return new BlockContextImpl(block, false); + } + + /** + * Creates a player-specific context. + * + * @param player the player to be used as the holder of the context. + * @param threadSafe is the created map thread safe + * @return a new Context instance with the specified player as the holder. + */ + static Context player(@Nullable Player player, boolean threadSafe) { + return new PlayerContextImpl(player, threadSafe); + } + + /** + * Creates a block-specific context. + * + * @param block the block to be used as the holder of the context. + * @param threadSafe is the created map thread safe + * @return a new Context instance with the specified block as the holder. + */ + static Context block(@NotNull CustomCropsBlockState block, boolean threadSafe) { + return new BlockContextImpl(block, threadSafe); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java new file mode 100644 index 000000000..9c69bb428 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java @@ -0,0 +1,103 @@ +/* + * 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.context; + +import org.bukkit.Location; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.Objects; + +/** + * Represents keys for accessing context values with specific types. + * + * @param the type of the value associated with the context key. + */ +public class ContextKeys { + + public static final ContextKeys LOCATION = of("location", Location.class); + public static final ContextKeys X = of("x", Integer.class); + public static final ContextKeys Y = of("y", Integer.class); + public static final ContextKeys Z = of("z", Integer.class); + public static final ContextKeys WORLD = of("world", String.class); + public static final ContextKeys PLAYER = of("player", String.class); + public static final ContextKeys SLOT = of("slot", EquipmentSlot.class); + public static final ContextKeys TEMP_NEAR_PLAYER = of("near", String.class); + public static final ContextKeys OFFLINE = of("offline", Boolean.class); + + private final String key; + private final Class type; + + protected ContextKeys(String key, Class type) { + this.key = key; + this.type = type; + } + + /** + * Gets the key. + * + * @return the key. + */ + public String key() { + return key; + } + + /** + * Gets the type associated with the key. + * + * @return the type. + */ + public Class type() { + return type; + } + + /** + * Creates a new context key. + * + * @param key the key. + * @param type the type. + * @param the type of the value. + * @return a new ContextKeys instance. + */ + public static ContextKeys of(String key, Class type) { + return new ContextKeys(key, type); + } + + @Override + public final boolean equals(final Object other) { + if (this == other) { + return true; + } else if (other != null && this.getClass() == other.getClass()) { + ContextKeys that = (ContextKeys) other; + return Objects.equals(this.key, that.key); + } else { + return false; + } + } + + @Override + public final int hashCode() { + return Objects.hashCode(this.key); + } + + @Override + public String toString() { + return "ContextKeys{" + + "key='" + key + '\'' + + '}'; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/PlayerContextImpl.java b/api/src/main/java/net/momirealms/customcrops/api/context/PlayerContextImpl.java new file mode 100644 index 000000000..679563be5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/context/PlayerContextImpl.java @@ -0,0 +1,45 @@ +/* + * 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.context; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +public final class PlayerContextImpl extends AbstractContext { + + public PlayerContextImpl(@Nullable Player player, boolean sync) { + super(player, sync); + if (player == null) return; + final Location location = player.getLocation(); + arg(ContextKeys.PLAYER, player.getName()) + .arg(ContextKeys.LOCATION, location) + .arg(ContextKeys.X, location.getBlockX()) + .arg(ContextKeys.Y, location.getBlockY()) + .arg(ContextKeys.Z, location.getBlockZ()) + .arg(ContextKeys.WORLD, location.getWorld().getName()); + } + + @Override + public String toString() { + return "PlayerContext{" + + "args=" + args() + + ", player=" + holder() + + '}'; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java new file mode 100644 index 000000000..098b70cde --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -0,0 +1,120 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.helper.VersionHelper; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.EntityType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.HashSet; +import java.util.List; + +public abstract class AbstractCustomEventListener implements Listener { + + private final HashSet entities = new HashSet<>(); + private final HashSet blocks = new HashSet<>(); + + protected final AbstractItemManager itemManager; + + public AbstractCustomEventListener(AbstractItemManager itemManager) { + this.itemManager = itemManager; + this.entities.addAll(List.of(EntityType.ITEM_FRAME, EntityType.ARMOR_STAND)); + if (VersionHelper.isVersionNewerThan1_19_4()) { + this.entities.addAll(List.of(EntityType.ITEM_DISPLAY, EntityType.INTERACTION)); + } + this.blocks.addAll(List.of( + Material.NOTE_BLOCK, + Material.MUSHROOM_STEM, Material.BROWN_MUSHROOM_BLOCK, Material.RED_MUSHROOM_BLOCK, + Material.TRIPWIRE, + Material.CHORUS_PLANT, Material.CHORUS_FLOWER, + Material.ACACIA_LEAVES, Material.BIRCH_LEAVES, Material.JUNGLE_LEAVES, Material.DARK_OAK_LEAVES, Material.AZALEA_LEAVES, Material.FLOWERING_AZALEA_LEAVES, Material.OAK_LEAVES, Material.SPRUCE_LEAVES, + Material.CAVE_VINES, Material.TWISTING_VINES, Material.WEEPING_VINES, + Material.KELP, + Material.CACTUS + )); + if (VersionHelper.isVersionNewerThan1_19()) { + this.blocks.add(Material.MANGROVE_LEAVES); + } + if (VersionHelper.isVersionNewerThan1_20()) { + this.blocks.add(Material.CHERRY_LEAVES); + } + } + + @EventHandler + public void onInteractAir(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_AIR) + return; + this.itemManager.handlePlayerInteractAir( + event.getPlayer(), + event.getHand(), event.getItem() + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractBlock(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) + return; + Block block = event.getClickedBlock(); + assert block != null; + if (blocks.contains(block.getType())) { + return; + } + this.itemManager.handlePlayerInteractBlock( + event.getPlayer(), + block, + block.getType().name(), event.getBlockFace(), + event.getHand(), + event.getItem(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractEntity(PlayerInteractAtEntityEvent event) { + EntityType type = event.getRightClicked().getType(); + if (entities.contains(type)) { + return; + } + this.itemManager.handlePlayerInteractFurniture( + event.getPlayer(), + event.getRightClicked().getLocation(), type.name(), + event.getHand(), event.getPlayer().getInventory().getItem(event.getHand()), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceBlock(BlockPlaceEvent event) { + Block block = event.getBlock(); + if (blocks.contains(block.getType())) { + return; + } + this.itemManager.handlePlayerPlace( + event.getPlayer(), + block.getLocation(), + block.getType().name(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakBlock(BlockBreakEvent event) { + Block block = event.getBlock(); + if (blocks.contains(block.getType())) { + return; + } + this.itemManager.handlePlayerBreak( + event.getPlayer(), + block.getLocation(), event.getPlayer().getInventory().getItemInMainHand(), block.getType().name(), + event + ); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java new file mode 100644 index 000000000..7000beff8 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java @@ -0,0 +1,55 @@ +package net.momirealms.customcrops.api.core; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +public abstract class AbstractItemManager implements ItemManager { + + public abstract void handlePlayerInteractAir( + Player player, + EquipmentSlot hand, + ItemStack itemInHand + ); + + public abstract void handlePlayerInteractBlock( + Player player, + Block block, + String blockID, + BlockFace blockFace, + EquipmentSlot hand, + ItemStack itemInHand, + Cancellable event + ); + + // it's not a good choice to use Entity as parameter because the entity might be fake + public abstract void handlePlayerInteractFurniture( + Player player, + Location location, + String furnitureID, + EquipmentSlot hand, + ItemStack itemInHand, + Cancellable event + ); + + public abstract void handlePlayerBreak( + Player player, + Location location, + ItemStack itemInHand, + String brokenID, + Cancellable event + ); + + public abstract void handlePlayerPlace( + Player player, + Location location, + String placedID, + EquipmentSlot hand, + ItemStack itemInHand, + Cancellable event + ); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java new file mode 100644 index 000000000..2a7a6ce74 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java @@ -0,0 +1,43 @@ +package net.momirealms.customcrops.api.core; + +import com.flowpowered.nbt.CompoundMap; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.common.util.Key; + +import java.util.Objects; + +public class BuiltInBlockMechanics { + + public static final BuiltInBlockMechanics CROP = create("crop"); + public static final BuiltInBlockMechanics SPRINKLER = create("sprinkler"); + public static final BuiltInBlockMechanics GREENHOUSE = create("greenhouse"); + public static final BuiltInBlockMechanics POT = create("pot"); + public static final BuiltInBlockMechanics SCARECROW = create("scarecrow"); + + private final Key key; + + public BuiltInBlockMechanics(Key key) { + this.key = key; + } + + static BuiltInBlockMechanics create(String id) { + return new BuiltInBlockMechanics(Key.key("customcrops", id)); + } + + public Key key() { + return key; + } + + public CustomCropsBlockState createBlockState() { + return mechanic().createBlockState(); + } + + public CustomCropsBlockState createBlockState(CompoundMap data) { + return mechanic().createBlockState(data); + } + + public CustomCropsBlock mechanic() { + return Objects.requireNonNull(Registries.BLOCK.get(key)); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java new file mode 100644 index 000000000..5e8dad986 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java @@ -0,0 +1,30 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.util.Key; +import net.momirealms.customcrops.api.core.item.CustomCropsItem; + +public class BuiltInItemMechanics { + + public static final BuiltInItemMechanics WATERING_CAN = create("watering_can"); + public static final BuiltInItemMechanics FERTILIZER = create("fertilizer"); + public static final BuiltInItemMechanics SEED = create("seed"); + public static final BuiltInItemMechanics SPRINKLER_ITEM = create("sprinkler_item"); + + private final Key key; + + public BuiltInItemMechanics(Key key) { + this.key = key; + } + + static BuiltInItemMechanics create(String id) { + return new BuiltInItemMechanics(Key.key("customcrops", id)); + } + + public Key key() { + return key; + } + + public CustomCropsItem mechanic() { + return Registries.ITEM.get(key); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java new file mode 100644 index 000000000..5fdb9bc0c --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java @@ -0,0 +1,17 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.util.Key; + +public class ClearableMappedRegistry extends MappedRegistry implements ClearableRegistry { + + public ClearableMappedRegistry(Key key) { + super(key); + } + + @Override + public void clear() { + super.byID.clear(); + super.byKey.clear(); + super.byValue.clear(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java new file mode 100644 index 000000000..e1c539e8a --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java @@ -0,0 +1,6 @@ +package net.momirealms.customcrops.api.core; + +public interface ClearableRegistry extends WriteableRegistry { + + void clear(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java new file mode 100644 index 000000000..4b99df846 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -0,0 +1,422 @@ +package net.momirealms.customcrops.api.core; + +import com.google.common.base.Preconditions; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; +import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.common.ScalarStyle; +import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.nodes.Tag; +import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; +import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; +import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; +import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; +import dev.dejvokep.boostedyaml.utils.format.NodeRole; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.block.*; +import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.water.FillMethod; +import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.util.PluginUtils; +import net.momirealms.customcrops.common.config.ConfigLoader; +import net.momirealms.customcrops.common.plugin.feature.Reloadable; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.entity.Player; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public abstract class ConfigManager implements ConfigLoader, Reloadable { + + private static ConfigManager instance; + protected final BukkitCustomCropsPlugin plugin; + + protected boolean doubleCheck; + protected boolean metrics; + protected boolean checkUpdate; + protected boolean debug; + protected String absoluteWorldPath; + + protected Set scarecrow; + protected ExistenceForm scarecrowExistenceForm; + protected boolean enableScarecrow; + protected boolean protectOriginalLore; + protected int scarecrowRange; + protected boolean scarecrowProtectChunk; + + protected Set greenhouse; + protected boolean enableGreenhouse; + protected ExistenceForm greenhouseExistenceForm; + protected int greenhouseRange; + + protected boolean syncSeasons; + protected String referenceWorld; + + protected String[] itemDetectOrder; + + protected double[] defaultQualityRatio; + + protected boolean hasNamespace; + + public ConfigManager(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + instance = this; + } + + public static boolean syncSeasons() { + return instance.syncSeasons; + } + + public static String referenceWorld() { + return instance.referenceWorld; + } + + public static boolean enableGreenhouse() { + return instance.enableGreenhouse; + } + + public static ExistenceForm greenhouseExistenceForm() { + return instance.greenhouseExistenceForm; + } + + public static int greenhouseRange() { + return instance.greenhouseRange; + } + + public static int scarecrowRange() { + return instance.scarecrowRange; + } + + public static boolean enableScarecrow() { + return instance.enableScarecrow; + } + + public static boolean scarecrowProtectChunk() { + return instance.scarecrowProtectChunk; + } + + public static boolean debug() { + return instance.debug; + } + + public static boolean metrics() { + return instance.metrics; + } + + public static boolean checkUpdate() { + return instance.checkUpdate; + } + + public static String absoluteWorldPath() { + return instance.absoluteWorldPath; + } + + public static Set greenhouse() { + return instance.greenhouse; + } + + public static ExistenceForm scarecrowExistenceForm() { + return instance.scarecrowExistenceForm; + } + + public static boolean doubleCheck() { + return instance.doubleCheck; + } + + public static Set scarecrow() { + return instance.scarecrow; + } + + public static boolean protectOriginalLore() { + return instance.protectOriginalLore; + } + + public static String[] itemDetectOrder() { + return instance.itemDetectOrder; + } + + public static double[] defaultQualityRatio() { + return instance.defaultQualityRatio; + } + + public static boolean hasNamespace() { + return instance.hasNamespace; + } + + @Override + public YamlDocument loadConfig(String filePath) { + return loadConfig(filePath, '.'); + } + + @Override + public YamlDocument loadConfig(String filePath, char routeSeparator) { + try (InputStream inputStream = new FileInputStream(resolveConfig(filePath).toFile())) { + return YamlDocument.create( + inputStream, + plugin.getResourceStream(filePath), + GeneralSettings.builder().setRouteSeparator(routeSeparator).build(), + LoaderSettings + .builder() + .setAutoUpdate(true) + .build(), + DumperSettings.builder() + .setScalarFormatter((tag, value, role, def) -> { + if (role == NodeRole.KEY) { + return ScalarStyle.PLAIN; + } else { + return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN; + } + }) + .build(), + UpdaterSettings + .builder() + .setVersioning(new BasicVersioning("config-version")) + .build() + ); + } catch (IOException e) { + plugin.getPluginLogger().severe("Failed to load config " + filePath, e); + throw new RuntimeException(e); + } + } + + @Override + public YamlDocument loadData(File file) { + try (InputStream inputStream = new FileInputStream(file)) { + return YamlDocument.create(inputStream); + } catch (IOException e) { + plugin.getPluginLogger().severe("Failed to load config " + file, e); + throw new RuntimeException(e); + } + } + + @Override + public YamlDocument loadData(File file, char routeSeparator) { + try (InputStream inputStream = new FileInputStream(file)) { + return YamlDocument.create(inputStream, GeneralSettings.builder() + .setRouteSeparator(routeSeparator) + .build()); + } catch (IOException e) { + plugin.getPluginLogger().severe("Failed to load config " + file, e); + throw new RuntimeException(e); + } + } + + protected Path resolveConfig(String filePath) { + if (filePath == null || filePath.isEmpty()) { + throw new IllegalArgumentException("ResourcePath cannot be null or empty"); + } + filePath = filePath.replace('\\', '/'); + Path configFile = plugin.getConfigDirectory().resolve(filePath); + // if the config doesn't exist, create it based on the template in the resources dir + if (!Files.exists(configFile)) { + try { + Files.createDirectories(configFile.getParent()); + } catch (IOException e) { + // ignore + } + try (InputStream is = plugin.getResourceStream(filePath)) { + if (is == null) { + throw new IllegalArgumentException("The embedded resource '" + filePath + "' cannot be found"); + } + Files.copy(is, configFile); + addDefaultNamespace(configFile.toFile()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return configFile; + } + + public abstract void registerWateringCanConfig(WateringCanConfig config); + + public abstract void registerFertilizerConfig(FertilizerConfig config); + + public abstract void registerCropConfig(CropConfig config); + + public abstract void registerPotConfig(PotConfig config); + + public abstract void registerSprinklerConfig(SprinklerConfig config); + + public WateringMethod[] getWateringMethods(Section section) { + ArrayList methods = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + WateringMethod fillMethod = new WateringMethod( + Preconditions.checkNotNull(innerSection.getString("item"), "fill-method item should not be null"), + innerSection.getInt("item-amount", 1), + innerSection.getString("return"), + innerSection.getInt("return-amount", 1), + innerSection.getInt("amount", 1), + plugin.getActionManager(Player.class).parseActions(innerSection.getSection("actions")), + plugin.getRequirementManager(Player.class).parseRequirements(innerSection.getSection("requirements"), true) + ); + methods.add(fillMethod); + } + } + } + return methods.toArray(new WateringMethod[0]); + } + + public FillMethod[] getFillMethods(Section section) { + ArrayList methods = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + FillMethod fillMethod = new FillMethod( + Preconditions.checkNotNull(innerSection.getString("target"), "fill-method target should not be null"), + innerSection.getInt("amount", 1), + plugin.getActionManager(Player.class).parseActions(innerSection.getSection("actions")), + plugin.getRequirementManager(Player.class).parseRequirements(innerSection.getSection("requirements"), true) + ); + methods.add(fillMethod); + } + } + } + return methods.toArray(new FillMethod[0]); + } + + public HashMap getInt2IntMap(Section section) { + HashMap map = new HashMap<>(); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + try { + int i1 = Integer.parseInt(entry.getKey()); + if (entry.getValue() instanceof Number i2) { + map.put(i1, i2.intValue()); + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException(); + } + } + } + return map; + } + + public double[] getQualityRatio(String ratios) { + String[] split = ratios.split("/"); + double[] ratio = new double[split.length]; + double weightTotal = Arrays.stream(split).mapToInt(Integer::parseInt).sum(); + double temp = 0; + for (int i = 0; i < ratio.length; i++) { + temp += Integer.parseInt(split[i]); + ratio[i] = temp / weightTotal; + } + return ratio; + } + + public List> getIntChancePair(Section section) { + ArrayList> pairs = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + int point = Integer.parseInt(entry.getKey()); + if (entry.getValue() instanceof Number n) { + Pair pair = new Pair<>(n.doubleValue(), point); + pairs.add(pair); + } + } + } + return pairs; + } + + public HashMap> getFertilizedPotMap(Section section) { + HashMap> map = new HashMap<>(); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + FertilizerType type = Registries.FERTILIZER_TYPE.get(entry.getKey().replace("-", "_")); + if (type != null) { + map.put(type, Pair.of( + Preconditions.checkNotNull(innerSection.getString("dry"), entry.getKey() + ".dry should not be null"), + Preconditions.checkNotNull(innerSection.getString("wet"), entry.getKey() + ".wet should not be null") + )); + } + } + } + } + return map; + } + + public BoneMeal[] getBoneMeals(Section section) { + ArrayList boneMeals = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + BoneMeal boneMeal = new BoneMeal( + Preconditions.checkNotNull(innerSection.getString("item"), "Bone meal item can't be null"), + innerSection.getInt("item-amount",1), + innerSection.getString("return"), + innerSection.getInt("return-amount",1), + innerSection.getBoolean("dispenser",true), + getIntChancePair(innerSection.getSection("chance")), + plugin.getActionManager(Player.class).parseActions(innerSection.getSection("actions")) + ); + boneMeals.add(boneMeal); + } + } + } + return boneMeals.toArray(new BoneMeal[0]); + } + + public DeathCondition[] getDeathConditions(Section section, ExistenceForm original) { + ArrayList conditions = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + DeathCondition deathCondition = new DeathCondition( + plugin.getRequirementManager(CustomCropsBlockState.class).parseRequirements(inner.getSection("conditions"), false), + inner.getString("model"), + Optional.ofNullable(inner.getString("type")).map(ExistenceForm::valueOf).orElse(original), + inner.getInt("delay", 0) + ); + conditions.add(deathCondition); + } + } + } + return conditions.toArray(new DeathCondition[0]); + } + + public GrowCondition[] getGrowConditions(Section section) { + ArrayList conditions = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + GrowCondition growCondition = new GrowCondition( + plugin.getRequirementManager(CustomCropsBlockState.class).parseRequirements(inner.getSection("conditions"), false), + inner.getInt("point", 1) + ); + conditions.add(growCondition); + } + } + } + return conditions.toArray(new GrowCondition[0]); + } + + protected void addDefaultNamespace(File file) { + boolean hasNamespace = PluginUtils.isEnabled("ItemsAdder"); + String line; + StringBuilder sb = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + while ((line = reader.readLine()) != null) { + sb.append(line).append(System.lineSeparator()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { + String finalStr = sb.toString(); + if (!hasNamespace) { + finalStr = finalStr.replace("CHORUS", "TRIPWIRE").replace("", ""); + } + writer.write(finalStr.replace("{0}", hasNamespace ? "customcrops:" : "")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/CustomForm.java b/api/src/main/java/net/momirealms/customcrops/api/core/CustomForm.java new file mode 100644 index 000000000..c3d5e01d4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/CustomForm.java @@ -0,0 +1,22 @@ +package net.momirealms.customcrops.api.core; + +public enum CustomForm { + + TRIPWIRE(ExistenceForm.BLOCK), + NOTE_BLOCK(ExistenceForm.BLOCK), + MUSHROOM(ExistenceForm.BLOCK), + CHORUS(ExistenceForm.BLOCK), + ITEM_FRAME(ExistenceForm.FURNITURE), + ITEM_DISPLAY(ExistenceForm.FURNITURE), + ARMOR_STAND(ExistenceForm.FURNITURE); + + private final ExistenceForm form; + + CustomForm(ExistenceForm form) { + this.form = form; + } + + public ExistenceForm existenceForm() { + return form; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java b/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java new file mode 100644 index 000000000..eaf913bbf --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java @@ -0,0 +1,34 @@ +package net.momirealms.customcrops.api.core; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; + +public interface CustomItemProvider { + + boolean removeCustomBlock(Location location); + + boolean placeCustomBlock(Location location, String id); + + @Nullable + Entity placeFurniture(Location location, String id); + + boolean removeFurniture(Entity entity); + + @Nullable + String blockID(Block block); + + @Nullable + String itemID(ItemStack itemStack); + + @Nullable + ItemStack itemStack(Player player, String id); + + @Nullable + String furnitureID(Entity entity); + + boolean isFurniture(Entity entity); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ExistenceForm.java b/api/src/main/java/net/momirealms/customcrops/api/core/ExistenceForm.java new file mode 100644 index 000000000..1a3a53d40 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ExistenceForm.java @@ -0,0 +1,8 @@ +package net.momirealms.customcrops.api.core; + +public enum ExistenceForm { + + BLOCK, + FURNITURE, + ANY +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java b/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java new file mode 100644 index 000000000..481776e03 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java @@ -0,0 +1,79 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.util.RandomUtils; +import org.bukkit.Rotation; + +public enum FurnitureRotation { + + NONE(0f), + EAST(-90f), + SOUTH(0f), + WEST(90f), + NORTH(180f); + + private final float yaw; + + FurnitureRotation(float yaw) { + this.yaw = yaw; + } + + public float getYaw() { + return yaw; + } + + public static FurnitureRotation random() { + return FurnitureRotation.values()[RandomUtils.generateRandomInt(1, 4)]; + } + + public static FurnitureRotation getByRotation(Rotation rotation) { + switch (rotation) { + default -> { + return FurnitureRotation.SOUTH; + } + case CLOCKWISE -> { + return FurnitureRotation.WEST; + } + case COUNTER_CLOCKWISE -> { + return FurnitureRotation.EAST; + } + case FLIPPED -> { + return FurnitureRotation.NORTH; + } + } + } + + public static FurnitureRotation getByYaw(float yaw) { + yaw = (Math.abs(yaw + 180) % 360); + switch ((int) (yaw/90)) { + case 1 -> { + return FurnitureRotation.WEST; + } + case 2 -> { + return FurnitureRotation.NORTH; + } + case 3 -> { + return FurnitureRotation.EAST; + } + default -> { + return FurnitureRotation.SOUTH; + } + } + } + + public Rotation getBukkitRotation() { + switch (this) { + case EAST -> { + return Rotation.COUNTER_CLOCKWISE; + } + case WEST -> { + return Rotation.CLOCKWISE; + } + case NORTH -> { + return Rotation.FLIPPED; + } + default -> { + return Rotation.NONE; + } + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java b/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java new file mode 100644 index 000000000..4dded1c94 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java @@ -0,0 +1,32 @@ +package net.momirealms.customcrops.api.core; + +import javax.annotation.Nullable; + +public interface IdMap extends Iterable { + int DEFAULT = -1; + + int getId(T value); + + @Nullable + T byId(int index); + + default T byIdOrThrow(int index) { + T object = this.byId(index); + if (object == null) { + throw new IllegalArgumentException("No value with id " + index); + } else { + return object; + } + } + + default int getIdOrThrow(T value) { + int i = this.getId(value); + if (i == -1) { + throw new IllegalArgumentException("Can't find id for '" + value + "' in map " + this); + } else { + return i; + } + } + + int size(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java b/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java new file mode 100644 index 000000000..f2af6f7d0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java @@ -0,0 +1,8 @@ +package net.momirealms.customcrops.api.core; + +public enum InteractionResult { + + SUCCESS, + FAIL, + PASS +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java new file mode 100644 index 000000000..bedbaca74 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java @@ -0,0 +1,63 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.item.Item; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ItemManager { + + void place(@NotNull Location location, @NotNull ExistenceForm form, @NotNull String id, FurnitureRotation rotation); + + FurnitureRotation remove(@NotNull Location location, @NotNull ExistenceForm form); + + void placeBlock(@NotNull Location location, @NotNull String id); + + void placeFurniture(@NotNull Location location, @NotNull String id, FurnitureRotation rotation); + + void removeBlock(@NotNull Location location); + + @Nullable + FurnitureRotation removeFurniture(@NotNull Location location); + + @NotNull + default String blockID(@NotNull Location location) { + return blockID(location.getBlock()); + } + + @NotNull + String blockID(@NotNull Block block); + + @Nullable + String furnitureID(@NotNull Entity entity); + + @NotNull String entityID(@NotNull Entity entity); + + @Nullable + String furnitureID(Location location); + + @NotNull + String anyID(Location location); + + @Nullable + String id(Location location, ExistenceForm form); + + void setCustomEventListener(@NotNull AbstractCustomEventListener listener); + + void setCustomItemProvider(@NotNull CustomItemProvider provider); + + String id(ItemStack itemStack); + + @Nullable + ItemStack build(Player player, String id); + + Item wrap(ItemStack itemStack); + + void decreaseDamage(Player player, ItemStack itemStack, int amount); + + void increaseDamage(Player holder, ItemStack itemStack, int amount); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java new file mode 100644 index 000000000..0d576e028 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java @@ -0,0 +1,58 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class MappedRegistry implements WriteableRegistry { + + protected final Map byKey = new HashMap<>(1024); + protected final Map byValue = new IdentityHashMap<>(1024); + protected final ArrayList byID = new ArrayList<>(1024); + private final Key key; + + public MappedRegistry(Key key) { + this.key = key; + } + + @Override + public void register(K key, T value) { + byKey.put(key, value); + byValue.put(value, key); + } + + @Override + public Key key() { + return key; + } + + @Override + public int getId(@Nullable T value) { + return byID.indexOf(value); + } + + @Nullable + @Override + public T byId(int index) { + return byID.get(index); + } + + @Override + public int size() { + return byKey.size(); + } + + @Nullable + @Override + public T get(@Nullable K key) { + return byKey.get(key); + } + + @NotNull + @Override + public Iterator iterator() { + return this.byKey.values().iterator(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java new file mode 100644 index 000000000..e84e4de3e --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java @@ -0,0 +1,42 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.item.CustomCropsItem; +import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.common.annotation.DoNotUse; +import net.momirealms.customcrops.common.util.Key; +import org.jetbrains.annotations.ApiStatus; + +import java.util.List; + +@ApiStatus.Internal +public class Registries { + + @DoNotUse + public static final WriteableRegistry BLOCK = new MappedRegistry<>(Key.key("mechanic", "block")); + @DoNotUse + public static final WriteableRegistry ITEM = new MappedRegistry<>(Key.key("mechanic", "item")); + @DoNotUse + public static final WriteableRegistry FERTILIZER_TYPE = new ClearableMappedRegistry<>(Key.key("mechanic", "fertilizer_type")); + @DoNotUse + public static final ClearableRegistry BLOCKS = new ClearableMappedRegistry<>(Key.key("internal", "blocks")); + @DoNotUse + public static final ClearableRegistry ITEMS = new ClearableMappedRegistry<>(Key.key("internal", "items")); + + public static final ClearableRegistry SPRINKLER = new ClearableMappedRegistry<>(Key.key("config", "sprinkler")); + public static final ClearableRegistry POT = new ClearableMappedRegistry<>(Key.key("config", "pot")); + public static final ClearableRegistry CROP = new ClearableMappedRegistry<>(Key.key("config", "crop")); + public static final ClearableRegistry FERTILIZER = new ClearableMappedRegistry<>(Key.key("config", "fertilizer")); + public static final ClearableRegistry WATERING_CAN = new ClearableMappedRegistry<>(Key.key("config", "watering_can")); + + public static final ClearableRegistry SEED_TO_CROP = new ClearableMappedRegistry<>(Key.key("fast_lookup", "seed_to_crop")); + public static final ClearableRegistry> STAGE_TO_CROP_UNSAFE = new ClearableMappedRegistry<>(Key.key("fast_lookup", "stage_to_crop")); + public static final ClearableRegistry ITEM_TO_FERTILIZER = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_fertilizer")); + public static final ClearableRegistry ITEM_TO_POT = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_pot")); + public static final ClearableRegistry ITEM_TO_SPRINKLER = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_sprinkler")); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java new file mode 100644 index 000000000..d092e7107 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java @@ -0,0 +1,16 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.util.Key; + +import javax.annotation.Nullable; + +public interface Registry extends IdMap { + + Key key(); + + @Override + int getId(@Nullable T value); + + @Nullable + T get(@Nullable K key); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java new file mode 100644 index 000000000..923cae2fe --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java @@ -0,0 +1,21 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.util.Key; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.item.CustomCropsItem; +import net.momirealms.customcrops.api.core.item.FertilizerType; + +public interface RegistryAccess { + + void registerBlockMechanic(CustomCropsBlock block); + + void registerItemMechanic(CustomCropsItem item); + + void registerFertilizerType(FertilizerType type); + + Registry getBlockRegistry(); + + Registry getItemRegistry(); + + Registry getFertilizerTypeRegistry(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java new file mode 100644 index 000000000..861227c04 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java @@ -0,0 +1,54 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.common.util.Key; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.item.CustomCropsItem; +import net.momirealms.customcrops.api.core.item.FertilizerType; + +public class SimpleRegistryAccess implements RegistryAccess { + + private BukkitCustomCropsPlugin plugin; + private boolean frozen; + + public SimpleRegistryAccess(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + } + + public void freeze() { + this.frozen = true; + } + + @Override + public void registerBlockMechanic(CustomCropsBlock block) { + if (frozen) throw new RuntimeException("Registries are frozen"); + Registries.BLOCK.register(block.type(), block); + } + + @Override + public void registerItemMechanic(CustomCropsItem item) { + if (frozen) throw new RuntimeException("Registries are frozen"); + Registries.ITEM.register(item.type(), item); + } + + @Override + public void registerFertilizerType(FertilizerType type) { + if (frozen) throw new RuntimeException("Registries are frozen"); + Registries.FERTILIZER_TYPE.register(type.id(), type); + } + + @Override + public Registry getBlockRegistry() { + return Registries.BLOCK; + } + + @Override + public Registry getItemRegistry() { + return Registries.ITEM; + } + + @Override + public Registry getFertilizerTypeRegistry() { + return Registries.FERTILIZER_TYPE; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java new file mode 100644 index 000000000..f04bd0d1d --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java @@ -0,0 +1,9 @@ +package net.momirealms.customcrops.api.core; + +import net.momirealms.customcrops.api.context.Context; + +@FunctionalInterface +public interface StatedItem { + + String currentState(Context context); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java similarity index 87% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java rename to api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java index 0a9c8b633..55fd9797f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.world; +package net.momirealms.customcrops.api.core; import com.flowpowered.nbt.CompoundMap; import com.flowpowered.nbt.Tag; @@ -37,7 +37,7 @@ public SynchronizedCompoundMap(CompoundMap compoundMap) { this.compoundMap = compoundMap; } - public CompoundMap getOriginalMap() { + public CompoundMap originalMap() { return compoundMap; } @@ -59,6 +59,15 @@ public Tag put(String key, Tag tag) { } } + public Tag remove(String key) { + writeLock.lock(); + try { + return compoundMap.remove(key); + } finally { + writeLock.unlock(); + } + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -78,16 +87,16 @@ private String compoundMapToString(CompoundMap compoundMap) { Tag tag = entry.getValue(); String tagValue; switch (tag.getType()) { - case TAG_STRING, TAG_BYTE, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_INT_ARRAY, TAG_LONG, TAG_SHORT, TAG_SHORT_ARRAY, TAG_LONG_ARRAY, TAG_BYTE_ARRAY -> + case TAG_STRING, TAG_BYTE, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_INT_ARRAY, TAG_LONG, TAG_SHORT, TAG_SHORT_ARRAY, TAG_LONG_ARRAY, TAG_BYTE_ARRAY, + TAG_LIST -> tagValue = tag.getValue().toString(); - case TAG_COMPOUND -> tagValue = compoundMapToString(tag.getAsCompoundTag().get().getValue()); - case TAG_LIST -> tagValue = tag.getAsListTag().get().getValue().toString(); + case TAG_COMPOUND -> tagValue = compoundMapToString((CompoundMap) tag.getValue()); default -> { continue; } } joiner.add("\"" + entry.getKey() + "\":\"" + tagValue + "\""); } - return "{" + joiner + "}"; + return "BlockData{" + joiner + "}"; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java new file mode 100644 index 000000000..a839a3272 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java @@ -0,0 +1,6 @@ +package net.momirealms.customcrops.api.core; + +public interface WriteableRegistry extends Registry { + + void register(K key, T value); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java new file mode 100644 index 000000000..8424c8c99 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java @@ -0,0 +1,82 @@ +package net.momirealms.customcrops.api.core.block; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.IntTag; +import com.flowpowered.nbt.StringTag; +import com.flowpowered.nbt.Tag; +import net.momirealms.customcrops.common.util.Key; +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.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; + +public abstract class AbstractCustomCropsBlock implements CustomCropsBlock { + + private final Key type; + + public AbstractCustomCropsBlock(Key type) { + this.type = type; + } + + @Override + public Key type() { + return type; + } + + @Override + public CustomCropsBlockState createBlockState() { + return CustomCropsBlockState.create(this, new CompoundMap()); + } + + @Override + public CustomCropsBlockState createBlockState(CompoundMap compoundMap) { + return CustomCropsBlockState.create(this, compoundMap); + } + + public String id(CustomCropsBlockState state) { + return state.get("key").getAsStringTag() + .map(StringTag::getValue) + .orElse(""); + } + + public void id(CustomCropsBlockState state, String id) { + state.set("key", new StringTag("key", id)); + } + + protected boolean canTick(CustomCropsBlockState state, int interval) { + if (interval <= 0) return false; + if (interval == 1) return true; + Tag tag = state.get("tick"); + int tick = 0; + if (tag != null) tick = tag.getAsIntTag().map(IntTag::getValue).orElse(0); + if (++tick >= interval) { + state.set("tick", new IntTag("tick", 0)); + return true; + } else { + state.set("tick", new IntTag("tick", tick)); + return false; + } + } + + @Override + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + } + + @Override + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + } + + @Override + public void onInteract(WrappedInteractEvent event) { + } + + @Override + public void onBreak(WrappedBreakEvent event) { + } + + @Override + public void onPlace(WrappedPlaceEvent event) { + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java similarity index 55% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java rename to api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java index 7883c7fbe..2dc206dad 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java @@ -15,67 +15,53 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.item; +package net.momirealms.customcrops.api.core.block; -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.manager.ActionManager; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.entity.Player; import java.util.List; public class BoneMeal { private final String item; - private final int usedAmount; + private final int requiredAmount; private final String returned; private final int returnedAmount; private final List> pointGainList; - private final Action[] actions; + private final Action[] actions; private final boolean dispenserAllowed; public BoneMeal( String item, - int usedAmount, + int requiredAmount, String returned, int returnedAmount, boolean dispenserAllowed, List> pointGainList, - Action[] actions + Action[] actions ) { this.item = item; this.returned = returned; this.pointGainList = pointGainList; this.actions = actions; - this.usedAmount = usedAmount; + this.requiredAmount = requiredAmount; this.returnedAmount = returnedAmount; this.dispenserAllowed = dispenserAllowed; } - /** - * Get the ID of the bone meal item - * - * @return bonemeal item id - */ - public String getItem() { + public String requiredItem() { return item; } - /** - * Get the returned item's ID - * - * @return returned item ID - */ - public String getReturned() { + public String returnedItem() { return returned; } - /** - * Get the points to gain in one try - * - * @return points - */ - public int getPoint() { + public int rollPoint() { for (Pair pair : pointGainList) { if (Math.random() < pair.left()) { return pair.right(); @@ -84,38 +70,18 @@ public int getPoint() { return 0; } - /** - * Trigger the actions of using the bone meal - * - * @param state player state - */ - public void trigger(State state) { - ActionManager.triggerActions(state, actions); + public void triggerActions(Context context) { + ActionManager.trigger(context, actions); } - /** - * Get the amount to consume - * - * @return amount to consume - */ - public int getUsedAmount() { - return usedAmount; + public int amountOfRequiredItem() { + return requiredAmount; } - /** - * Get the amount of the returned items - * - * @return amount of the returned items - */ - public int getReturnedAmount() { + public int amountOfReturnItem() { return returnedAmount; } - /** - * If the bone meal can be used with a dispenser - * - * @return can be used or not - */ public boolean isDispenserAllowed() { return dispenserAllowed; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/BreakReason.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/BreakReason.java new file mode 100644 index 000000000..f2b85a756 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/BreakReason.java @@ -0,0 +1,12 @@ +package net.momirealms.customcrops.api.core.block; + +public enum BreakReason { + // Crop was broken by a player + BREAK, + // Crop was trampled + TRAMPLE, + // Crop was broken due to an explosion (block or entity) + EXPLODE, + // Crop was broken due to a specific action + ACTION +} 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 new file mode 100644 index 000000000..514336805 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java @@ -0,0 +1,398 @@ +package net.momirealms.customcrops.api.core.block; + +import com.flowpowered.nbt.IntTag; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.item.Fertilizer; +import net.momirealms.customcrops.api.core.item.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.wrapper.WrappedBreakEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; +import net.momirealms.customcrops.api.event.BoneMealUseEvent; +import net.momirealms.customcrops.api.event.CropBreakEvent; +import net.momirealms.customcrops.api.event.CropInteractEvent; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.PlayerUtils; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class CropBlock extends AbstractCustomCropsBlock { + + public CropBlock() { + super(BuiltInBlockMechanics.CROP.key()); + } + + @Override + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + if (!world.setting().randomTickCrop() && canTick(state, world.setting().tickCropInterval())) { + tickCrop(state, world, location); + } + } + + @Override + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + if (world.setting().randomTickCrop() && canTick(state, world.setting().tickCropInterval())) { + tickCrop(state, world, location); + } + } + + @Override + public void onBreak(WrappedBreakEvent event) { + List configs = Registries.STAGE_TO_CROP_UNSAFE.get(event.brokenID()); + CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(event.location()); + if (configs == null || configs.isEmpty()) { + world.removeBlockState(pos3); + return; + } + + CustomCropsBlockState state = fixOrGetState(world, pos3, event.brokenID()); + if (state == null) { + return; + } + + // check data for precise data + CropConfig cropConfig = config(state); + // the config not exists or it's a wrong one + if (cropConfig == null || !configs.contains(cropConfig)) { + if (configs.size() != 1) { + return; + } + cropConfig = configs.get(0); + } + + CropStageConfig stageConfig = cropConfig.stageByID(event.brokenID()); + assert stageConfig != null; + + final Player player = event.playerBreaker(); + Context context = Context.player(player); + + // check requirements + if (!RequirementManager.isSatisfied(context, cropConfig.breakRequirements())) { + event.setCancelled(true); + return; + } + if (!RequirementManager.isSatisfied(context, stageConfig.breakRequirements())) { + event.setCancelled(true); + return; + } + + CropBreakEvent breakEvent = new CropBreakEvent(event.entityBreaker(), event.blockBreaker(), cropConfig, event.brokenID(), event.location(), + state, BreakReason.BREAK); + if (EventUtils.fireAndCheckCancel(breakEvent)) { + event.setCancelled(true); + return; + } + + ActionManager.trigger(context, stageConfig.breakActions()); + ActionManager.trigger(context, cropConfig.breakActions()); + world.removeBlockState(pos3); + } + + /** + * This can only be triggered admins because players shouldn't get the stages + */ + @Override + public void onPlace(WrappedPlaceEvent event) { + List configs = Registries.STAGE_TO_CROP_UNSAFE.get(event.placedID()); + if (configs == null || configs.size() != 1) { + return; + } + + CropConfig cropConfig = configs.get(0); + Context context = Context.player(event.player()); +// if (!RequirementManager.isSatisfied(context, cropConfig.plantRequirements())) { +// event.setCancelled(true); +// return; +// } + + Pos3 pos3 = Pos3.from(event.location()); + CustomCropsWorld world = event.world(); + if (world.setting().cropPerChunk() >= 0) { + if (world.testChunkLimitation(pos3, this.getClass(), world.setting().cropPerChunk())) { + ActionManager.trigger(context, cropConfig.reachLimitActions()); + event.setCancelled(true); + return; + } + } + + fixOrGetState(world, pos3, event.placedID()); + } + + @Override + public void onInteract(WrappedInteractEvent event) { + final Player player = event.player(); + Context context = Context.player(player); + + // data first + CustomCropsWorld world = event.world(); + Location location = event.location(); + Pos3 pos3 = Pos3.from(location); + // fix if possible + CustomCropsBlockState state = fixOrGetState(world, pos3, event.relatedID()); + if (state == null) return; + + CropConfig cropConfig = config(state); + if (!RequirementManager.isSatisfied(context, cropConfig.interactRequirements())) { + return; + } + + int point = point(state); + CropStageConfig stageConfig = cropConfig.getFloorStageEntry(point).getValue(); + if (!RequirementManager.isSatisfied(context, stageConfig.interactRequirements())) { + return; + } + + final ItemStack itemInHand = event.itemInHand(); + // trigger event + CropInteractEvent interactEvent = new CropInteractEvent(player, itemInHand, location, state, event.hand(), cropConfig, event.relatedID()); + if (EventUtils.fireAndCheckCancel(interactEvent)) { + return; + } + + Location potLocation = location.clone().subtract(0,1,0); + String blockBelowID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(potLocation.getBlock()); + PotConfig potConfig = Registries.ITEM_TO_POT.get(blockBelowID); + if (potConfig != null) { + PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); + assert potBlock != null; + // fix or get data + CustomCropsBlockState potState = potBlock.fixOrGetState(world, Pos3.from(potLocation), potConfig, event.relatedID()); + if (potBlock.tryWateringPot(player, context, potState, event.hand(), event.itemID(), potConfig, potLocation, itemInHand)) + return; + } + + if (point < cropConfig.maxPoints()) { + for (BoneMeal boneMeal : cropConfig.boneMeals()) { + if (boneMeal.requiredItem().equals(event.itemID()) && boneMeal.amountOfRequiredItem() <= itemInHand.getAmount()) { + BoneMealUseEvent useEvent = new BoneMealUseEvent(player, itemInHand, location, boneMeal, state, event.hand(), cropConfig); + if (EventUtils.fireAndCheckCancel(useEvent)) + return; + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemInHand.getAmount() - boneMeal.amountOfRequiredItem()); + if (boneMeal.returnedItem() != null) { + ItemStack returned = BukkitCustomCropsPlugin.getInstance().getItemManager().build(player, boneMeal.returnedItem()); + if (returned != null) { + PlayerUtils.giveItem(player, returned, boneMeal.amountOfReturnItem()); + } + } + } + boneMeal.triggerActions(context); + + int afterPoints = Math.min(point + boneMeal.rollPoint(), cropConfig.maxPoints()); + point(state, afterPoints); + + String afterStage = null; + ExistenceForm afterForm = null; + int tempPoints = afterPoints; + while (tempPoints >= 0) { + Map.Entry afterEntry = cropConfig.getFloorStageEntry(tempPoints); + CropStageConfig after = afterEntry.getValue(); + if (after.stageID() != null) { + afterStage = after.stageID(); + afterForm = after.existenceForm(); + break; + } + tempPoints = after.point() - 1; + } + + Objects.requireNonNull(afterForm); + Objects.requireNonNull(afterStage); + + Context blockContext = Context.block(state); + for (int i = point + 1; i <= afterPoints; i++) { + CropStageConfig stage = cropConfig.stageByPoint(i); + if (stage != null) { + ActionManager.trigger(blockContext, stage.growActions()); + } + } + + if (Objects.equals(afterStage, event.relatedID())) return; + Location bukkitLocation = location.toLocation(world.bukkitWorld()); + FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); + if (rotation == FurnitureRotation.NONE && cropConfig.rotation()) { + rotation = FurnitureRotation.random(); + } + BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, afterForm, afterStage, rotation); + return; + } + } + } + + ActionManager.trigger(context, cropConfig.interactActions()); + ActionManager.trigger(context, stageConfig.interactActions()); + } + + public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, String stageID) { + List configList = Registries.STAGE_TO_CROP_UNSAFE.get(stageID); + if (configList == null) return null; + + Optional optionalPotState = world.getBlockState(pos3); + if (optionalPotState.isPresent()) { + CustomCropsBlockState potState = optionalPotState.get(); + if (potState.type() instanceof CropBlock cropBlock) { + if (configList.stream().map(CropConfig::id).toList().contains(cropBlock.id(potState))) { + return potState; + } + } + } + + if (configList.size() != 1) { + return null; + } + CropConfig cropConfig = configList.get(0); + CropStageConfig stageConfig = cropConfig.stageByID(stageID); + int point = stageConfig.point(); + CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState(); + point(state, point); + id(state, cropConfig.id()); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + return state; + } + + private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + CropConfig config = config(state); + if (config == null) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Crop data is removed at location[" + world.worldName() + "," + location + "] because the crop config[" + id(state) + "] has been removed."); + world.removeBlockState(location); + return; + } + + int previousPoint = point(state); + World bukkitWorld = world.bukkitWorld(); + if (ConfigManager.doubleCheck()) { + Map.Entry nearest = config.getFloorStageEntry(previousPoint); + String blockID = BukkitCustomCropsPlugin.getInstance().getItemManager().id(location.toLocation(bukkitWorld), nearest.getValue().existenceForm()); + if (!config.stageIDs().contains(blockID)) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Crop[" + config.id() + "] is removed at location[" + world.worldName() + "," + location + "] because the id of the block is [" + blockID + "]"); + world.removeBlockState(location); + return; + } + } + + Context context = Context.block(state); + for (DeathCondition deathCondition : config.deathConditions()) { + if (deathCondition.isMet(context)) { + Location bukkitLocation = location.toLocation(bukkitWorld); + BukkitCustomCropsPlugin.getInstance().getScheduler().sync().runLater(() -> { + FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); + world.removeBlockState(location); + Optional.ofNullable(deathCondition.deathStage()).ifPresent(it -> { + BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, deathCondition.existenceForm(), it, rotation); + }); + }, deathCondition.deathDelay(), bukkitLocation); + return; + } + } + + if (previousPoint >= config.maxPoints()) { + return; + } + + int pointToAdd = 1; + for (GrowCondition growCondition : config.growConditions()) { + if (growCondition.isMet(context)) { + pointToAdd = growCondition.pointToAdd(); + break; + } + } + + Optional optionalState = world.getBlockState(location.add(0,-1,0)); + if (optionalState.isPresent()) { + CustomCropsBlockState belowState = optionalState.get(); + if (belowState.type() instanceof PotBlock potBlock) { + for (Fertilizer fertilizer : potBlock.fertilizers(belowState)) { + FertilizerConfig fertilizerConfig = fertilizer.config(); + if (fertilizerConfig != null) { + pointToAdd = fertilizerConfig.processGainPoints(pointToAdd); + } + } + } + } + + int afterPoints = Math.min(previousPoint + pointToAdd, config.maxPoints()); + point(state, afterPoints); + + int tempPoints = previousPoint; + String preStage = null; + while (tempPoints >= 0) { + Map.Entry preEntry = config.getFloorStageEntry(tempPoints); + CropStageConfig pre = preEntry.getValue(); + if (pre.stageID() != null) { + preStage = pre.stageID(); + break; + } + tempPoints = pre.point() - 1; + } + + String afterStage = null; + ExistenceForm afterForm = null; + tempPoints = afterPoints; + while (tempPoints >= 0) { + Map.Entry afterEntry = config.getFloorStageEntry(tempPoints); + CropStageConfig after = afterEntry.getValue(); + if (after.stageID() != null) { + afterStage = after.stageID(); + afterForm = after.existenceForm(); + break; + } + tempPoints = after.point() - 1; + } + + Location bukkitLocation = location.toLocation(bukkitWorld); + + final String finalPreStage = preStage; + final String finalAfterStage = afterStage; + final ExistenceForm finalAfterForm = afterForm; + + Objects.requireNonNull(finalAfterStage); + Objects.requireNonNull(finalPreStage); + Objects.requireNonNull(finalAfterForm); + + BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run(() -> { + for (int i = previousPoint + 1; i <= afterPoints; i++) { + CropStageConfig stage = config.stageByPoint(i); + if (stage != null) { + ActionManager.trigger(context, stage.growActions()); + } + } + if (Objects.equals(finalAfterStage, finalPreStage)) return; + FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); + if (rotation == FurnitureRotation.NONE && config.rotation()) { + rotation = FurnitureRotation.random(); + } + BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, finalAfterForm, finalAfterStage, rotation); + }, bukkitLocation); + } + + public int point(CustomCropsBlockState state) { + return state.get("point").getAsIntTag().map(IntTag::getValue).orElse(0); + } + + public void point(CustomCropsBlockState state, int point) { + state.set("point", new IntTag("point", point)); + } + + public CropConfig config(CustomCropsBlockState state) { + return Registries.CROP.get(id(state)); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java new file mode 100644 index 000000000..a917314d5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java @@ -0,0 +1,104 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public interface CropConfig { + + String id(); + + String seed(); + + int maxPoints(); + + Requirement[] plantRequirements(); + + Requirement[] breakRequirements(); + + Requirement[] interactRequirements(); + + GrowCondition[] growConditions(); + + Action[] wrongPotActions(); + + Action[] interactActions(); + + Action[] breakActions(); + + Action[] plantActions(); + + Action[] reachLimitActions(); + + Action[] deathActions(); + + DeathCondition[] deathConditions(); + + BoneMeal[] boneMeals(); + + boolean rotation(); + + Set potWhitelist(); + + CropStageConfig stageByPoint(int point); + + @Nullable + CropStageConfig stageByID(String stageModel); + + CropStageConfig stageWithModelByPoint(int point); + + Collection stages(); + + Collection stageIDs(); + + Map.Entry getFloorStageEntry(int previousPoint); + + static Builder builder() { + return new CropConfigImpl.BuilderImpl(); + } + + interface Builder { + + CropConfig build(); + + Builder id(String id); + + Builder seed(String seed); + + Builder maxPoints(int maxPoints); + + Builder wrongPotActions(Action[] wrongPotActions); + + Builder interactActions(Action[] interactActions); + + Builder breakActions(Action[] breakActions); + + Builder plantActions(Action[] plantActions); + + Builder reachLimitActions(Action[] reachLimitActions); + + Builder plantRequirements(Requirement[] plantRequirements); + + Builder breakRequirements(Requirement[] breakRequirements); + + Builder interactRequirements(Requirement[] interactRequirements); + + Builder growConditions(GrowCondition[] growConditions); + + Builder deathConditions(DeathCondition[] deathConditions); + + Builder boneMeals(BoneMeal[] boneMeals); + + Builder rotation(boolean rotation); + + Builder potWhitelist(Set whitelist); + + Builder stages(Collection stages); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java new file mode 100644 index 000000000..bc0554a19 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java @@ -0,0 +1,345 @@ +package net.momirealms.customcrops.api.core.block; + +import com.google.common.base.Preconditions; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.*; + +public class CropConfigImpl implements CropConfig { + + private final String id; + private final String seed; + private final int maxPoints; + private final Action[] wrongPotActions; + private final Action[] interactActions; + private final Action[] breakActions; + private final Action[] plantActions; + private final Action[] reachLimitActions; + private final Action[] deathActions; + private final Requirement[] plantRequirements; + private final Requirement[] breakRequirements; + private final Requirement[] interactRequirements; + private final GrowCondition[] growConditions; + private final DeathCondition[] deathConditions; + private final BoneMeal[] boneMeals; + private final boolean rotation; + private final Set potWhitelist; + private final HashMap point2Stages = new HashMap<>(); + private final NavigableMap navigablePoint2Stages = new TreeMap<>(); + private final HashMap id2Stages = new HashMap<>(); + private final Set stageIDs = new HashSet<>(); + private final HashMap cropStageWithModelMap = new HashMap<>(); + + public CropConfigImpl( + String id, + String seed, + int maxPoints, + Action[] wrongPotActions, + Action[] interactActions, + Action[] breakActions, + Action[] plantActions, + Action[] reachLimitActions, + Action[] deathActions, + Requirement[] plantRequirements, + Requirement[] breakRequirements, + Requirement[] interactRequirements, + GrowCondition[] growConditions, + DeathCondition[] deathConditions, + BoneMeal[] boneMeals, + boolean rotation, + Set potWhitelist, + Collection stageBuilders + ) { + this.id = id; + this.seed = seed; + this.maxPoints = maxPoints; + this.wrongPotActions = wrongPotActions; + this.interactActions = interactActions; + this.breakActions = breakActions; + this.plantActions = plantActions; + this.reachLimitActions = reachLimitActions; + this.deathActions = deathActions; + this.plantRequirements = plantRequirements; + this.breakRequirements = breakRequirements; + this.interactRequirements = interactRequirements; + this.growConditions = growConditions; + this.deathConditions = deathConditions; + this.boneMeals = boneMeals; + this.rotation = rotation; + this.potWhitelist = potWhitelist; + for (CropStageConfig.Builder builder : stageBuilders) { + CropStageConfig config = builder.crop(this).build(); + point2Stages.put(config.point(), config); + navigablePoint2Stages.put(config.point(), config); + String stageID = config.stageID(); + id2Stages.put(stageID, config); + stageIDs.add(stageID); + } + CropStageConfig tempConfig = null; + for (int i = 0; i <= maxPoints; i++) { + CropStageConfig config = point2Stages.get(i); + if (config != null) { + String stageModel = config.stageID(); + if (stageModel != null) { + tempConfig = config; + } + } + cropStageWithModelMap.put(i, tempConfig); + } + } + + @Override + public String id() { + return id; + } + + @Override + public String seed() { + return seed; + } + + @Override + public int maxPoints() { + return maxPoints; + } + + @Override + public Requirement[] plantRequirements() { + return plantRequirements; + } + + @Override + public Requirement[] breakRequirements() { + return breakRequirements; + } + + @Override + public Requirement[] interactRequirements() { + return interactRequirements; + } + + @Override + public GrowCondition[] growConditions() { + return growConditions; + } + + @Override + public Action[] wrongPotActions() { + return wrongPotActions; + } + + @Override + public Action[] interactActions() { + return interactActions; + } + + @Override + public Action[] breakActions() { + return breakActions; + } + + @Override + public Action[] plantActions() { + return plantActions; + } + + @Override + public Action[] reachLimitActions() { + return reachLimitActions; + } + + @Override + public Action[] deathActions() { + return deathActions; + } + + @Override + public DeathCondition[] deathConditions() { + return deathConditions; + } + + @Override + public BoneMeal[] boneMeals() { + return boneMeals; + } + + @Override + public boolean rotation() { + return rotation; + } + + @Override + public Set potWhitelist() { + return potWhitelist; + } + + @Override + public CropStageConfig stageByPoint(int id) { + return point2Stages.get(id); + } + + @Override + public CropStageConfig stageByID(String stageModel) { + return id2Stages.get(stageModel); + } + + @Override + public CropStageConfig stageWithModelByPoint(int point) { + return cropStageWithModelMap.get(Math.min(maxPoints, point)); + } + + @Override + public Collection stages() { + return point2Stages.values(); + } + + @Override + public Collection stageIDs() { + return stageIDs; + } + + @Override + public Map.Entry getFloorStageEntry(int point) { + Preconditions.checkArgument(point >= 0, "Point should be no lower than " + point); + return navigablePoint2Stages.floorEntry(point); + } + + public static class BuilderImpl implements Builder { + + private String id; + private String seed; + private ExistenceForm existenceForm; + private int maxPoints; + private Action[] wrongPotActions; + private Action[] interactActions; + private Action[] breakActions; + private Action[] plantActions; + private Action[] reachLimitActions; + private Action[] deathActions; + private Requirement[] plantRequirements; + private Requirement[] breakRequirements; + private Requirement[] interactRequirements; + private GrowCondition[] growConditions; + private DeathCondition[] deathConditions; + private BoneMeal[] boneMeals; + private boolean rotation; + private Set potWhitelist; + private Collection stages; + + @Override + public CropConfig build() { + return new CropConfigImpl(id, seed, maxPoints, wrongPotActions, interactActions, breakActions, plantActions, reachLimitActions, deathActions, plantRequirements, breakRequirements, interactRequirements, growConditions, deathConditions, boneMeals, rotation, potWhitelist, stages); + } + + @Override + public Builder id(String id) { + this.id = id; + return this; + } + + @Override + public Builder seed(String seed) { + this.seed = seed; + return this; + } + + @Override + public Builder maxPoints(int maxPoints) { + this.maxPoints = maxPoints; + return this; + } + + @Override + public Builder wrongPotActions(Action[] wrongPotActions) { + this.wrongPotActions = wrongPotActions; + return this; + } + + @Override + public Builder interactActions(Action[] interactActions) { + this.interactActions = interactActions; + return this; + } + + @Override + public Builder breakActions(Action[] breakActions) { + this.breakActions = breakActions; + return this; + } + + @Override + public Builder plantActions(Action[] plantActions) { + this.plantActions = plantActions; + return this; + } + + public Builder deathActions(Action[] deathActions) { + this.deathActions = deathActions; + return this; + } + + @Override + public Builder reachLimitActions(Action[] reachLimitActions) { + this.reachLimitActions = reachLimitActions; + return this; + } + + @Override + public Builder plantRequirements(Requirement[] plantRequirements) { + this.plantRequirements = plantRequirements; + return this; + } + + @Override + public Builder breakRequirements(Requirement[] breakRequirements) { + this.breakRequirements = breakRequirements; + return this; + } + + @Override + public Builder interactRequirements(Requirement[] interactRequirements) { + this.interactRequirements = interactRequirements; + return this; + } + + @Override + public Builder growConditions(GrowCondition[] growConditions) { + this.growConditions = growConditions; + return this; + } + + @Override + public Builder deathConditions(DeathCondition[] deathConditions) { + this.deathConditions = deathConditions; + return this; + } + + @Override + public Builder boneMeals(BoneMeal[] boneMeals) { + this.boneMeals = boneMeals; + return this; + } + + @Override + public Builder rotation(boolean rotation) { + this.rotation = rotation; + return this; + } + + @Override + public Builder potWhitelist(Set potWhitelist) { + this.potWhitelist = new HashSet<>(potWhitelist); + return this; + } + + @Override + public Builder stages(Collection stages) { + this.stages = new HashSet<>(stages); + return this; + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java new file mode 100644 index 000000000..beb9448f5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java @@ -0,0 +1,61 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +public interface CropStageConfig { + + CropConfig crop(); + + double displayInfoOffset(); + + @Nullable + String stageID(); + + int point(); + + Requirement[] interactRequirements(); + + Requirement[] breakRequirements(); + + Action[] interactActions(); + + Action[] breakActions(); + + Action[] growActions(); + + ExistenceForm existenceForm(); + + static Builder builder() { + return new CropStageConfigImpl.BuilderImpl(); + } + + interface Builder { + + CropStageConfig build(); + + Builder crop(CropConfig crop); + + Builder displayInfoOffset(double offset); + + Builder stageID(String id); + + Builder point(int i); + + Builder interactRequirements(Requirement[] requirements); + + Builder breakRequirements(Requirement[] requirements); + + Builder interactActions(Action[] actions); + + Builder breakActions(Action[] actions); + + Builder growActions(Action[] actions); + + Builder existenceForm(ExistenceForm existenceForm); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java new file mode 100644 index 000000000..46502899c --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java @@ -0,0 +1,176 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +public class CropStageConfigImpl implements CropStageConfig { + + private final CropConfig crop; + private final ExistenceForm existenceForm; + private final double offset; + private final String stageID; + private final int point; + private final Requirement[] interactRequirements; + private final Requirement[] breakRequirements; + private final Action[] interactActions; + private final Action[] breakActions; + private final Action[] growActions; + + public CropStageConfigImpl( + CropConfig crop, + ExistenceForm existenceForm, + double offset, + String stageID, + int point, + Requirement[] interactRequirements, + Requirement[] breakRequirements, + Action[] interactActions, + Action[] breakActions, + Action[] growActions + ) { + this.crop = crop; + this.existenceForm = existenceForm; + this.offset = offset; + this.stageID = stageID; + this.point = point; + this.interactRequirements = interactRequirements; + this.breakRequirements = breakRequirements; + this.interactActions = interactActions; + this.breakActions = breakActions; + this.growActions = growActions; + } + + @Override + public CropConfig crop() { + return crop; + } + + @Override + public double displayInfoOffset() { + return offset; + } + + @Nullable + @Override + public String stageID() { + return stageID; + } + + @Override + public int point() { + return point; + } + + @Override + public Requirement[] interactRequirements() { + return interactRequirements; + } + + @Override + public Requirement[] breakRequirements() { + return breakRequirements; + } + + @Override + public Action[] interactActions() { + return interactActions; + } + + @Override + public Action[] breakActions() { + return breakActions; + } + + @Override + public Action[] growActions() { + return growActions; + } + + @Override + public ExistenceForm existenceForm() { + return existenceForm; + } + + public static class BuilderImpl implements Builder { + + private CropConfig crop; + private ExistenceForm existenceForm; + private double offset; + private String stageID; + private int point; + private Requirement[] interactRequirements; + private Requirement[] breakRequirements; + private Action[] interactActions; + private Action[] breakActions; + private Action[] growActions; + + @Override + public CropStageConfig build() { + return new CropStageConfigImpl(crop, existenceForm, offset, stageID, point, interactRequirements, breakRequirements, interactActions, breakActions, growActions); + } + + @Override + public Builder crop(CropConfig crop) { + this.crop = crop; + return this; + } + + @Override + public Builder displayInfoOffset(double offset) { + this.offset = offset; + return this; + } + + @Override + public Builder stageID(String id) { + this.stageID = id; + return this; + } + + @Override + public Builder point(int i) { + this.point = i; + return this; + } + + @Override + public Builder interactRequirements(Requirement[] requirements) { + this.interactRequirements = requirements; + return this; + } + + @Override + public Builder breakRequirements(Requirement[] requirements) { + this.breakRequirements = requirements; + return this; + } + + @Override + public Builder interactActions(Action[] actions) { + this.interactActions = actions; + return this; + } + + @Override + public Builder breakActions(Action[] actions) { + this.breakActions = actions; + return this; + } + + @Override + public Builder growActions(Action[] actions) { + this.growActions = actions; + return this; + } + + @Override + public Builder existenceForm(ExistenceForm existenceForm) { + this.existenceForm = existenceForm; + return this; + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java new file mode 100644 index 000000000..cd06a01eb --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) <2022> + * + * 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.core.block; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customcrops.common.util.RandomUtils; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.feature.entity.armorstand.FakeArmorStand; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +public class CrowAttack { + + private SchedulerTask task; + private final Location dynamicLocation; + private final Location cropLocation; + private final Vector vectorDown; + private final Vector vectorUp; + private final Player[] viewers; + private int timer; + private final ItemStack flyModel; + private final ItemStack standModel; + + public CrowAttack(Location location, ItemStack flyModel, ItemStack standModel) { + ArrayList viewers = new ArrayList<>(); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= 48) { + viewers.add(player); + } + } + this.viewers = viewers.toArray(new Player[0]); + this.cropLocation = location.clone().add(RandomUtils.generateRandomDouble(-0.25, 0.25), 0, RandomUtils.generateRandomDouble(-0.25, 0.25)); + float yaw = RandomUtils.generateRandomInt(-180, 180); + this.cropLocation.setYaw(yaw); + this.flyModel = flyModel; + this.standModel = standModel; + this.dynamicLocation = cropLocation.clone().add((10 * Math.sin((Math.PI * yaw) / 180)), 10, (- 10 * Math.cos((Math.PI * yaw) / 180))); + this.dynamicLocation.setYaw(yaw); + Location relative = cropLocation.clone().subtract(dynamicLocation); + this.vectorDown = new Vector(relative.getX() / 100, -0.1, relative.getZ() / 100); + this.vectorUp = new Vector(relative.getX() / 100, 0.1, relative.getZ() / 100); + } + + public void start() { + if (this.viewers.length == 0) return; + FakeArmorStand fake1 = SparrowHeart.getInstance().createFakeArmorStand(dynamicLocation); + fake1.invisible(true); + fake1.small(true); + fake1.equipment(EquipmentSlot.HEAD, flyModel); + FakeArmorStand fake2 = SparrowHeart.getInstance().createFakeArmorStand(cropLocation); + fake1.invisible(true); + fake1.small(true); + fake1.equipment(EquipmentSlot.HEAD, standModel); + for (Player player : this.viewers) { + fake1.spawn(player); + } + this.task = BukkitCustomCropsPlugin.getInstance().getScheduler().asyncRepeating(() -> { + timer++; + if (timer < 100) { + dynamicLocation.add(vectorDown); + for (Player player : this.viewers) { + SparrowHeart.getInstance().sendClientSideTeleportEntity(player, dynamicLocation, false, fake1.entityID()); + } + } else if (timer == 100){ + for (Player player : this.viewers) { + fake1.destroy(player); + } + for (Player player : this.viewers) { + fake2.spawn(player); + } + } else if (timer == 150) { + for (Player player : this.viewers) { + fake2.destroy(player); + } + for (Player player : this.viewers) { + fake1.spawn(player); + } + } else if (timer > 150) { + dynamicLocation.add(vectorUp); + for (Player player : this.viewers) { + SparrowHeart.getInstance().sendClientSideTeleportEntity(player, dynamicLocation, false, fake1.entityID()); + } + } + if (timer > 300) { + for (Player player : this.viewers) { + fake1.destroy(player); + } + task.cancel(); + } + }, 50, 50, TimeUnit.MILLISECONDS); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java new file mode 100644 index 000000000..9515fc30b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java @@ -0,0 +1,29 @@ +package net.momirealms.customcrops.api.core.block; + +import com.flowpowered.nbt.CompoundMap; +import net.momirealms.customcrops.common.util.Key; +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.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; + +public interface CustomCropsBlock { + + Key type(); + + CustomCropsBlockState createBlockState(); + + CustomCropsBlockState createBlockState(CompoundMap data); + + void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location); + + void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location); + + void onInteract(WrappedInteractEvent event); + + void onBreak(WrappedBreakEvent event); + + void onPlace(WrappedPlaceEvent event); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java new file mode 100644 index 000000000..e15a32d10 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java @@ -0,0 +1,40 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import org.jetbrains.annotations.Nullable; + +public class DeathCondition { + + private final Requirement[] requirements; + private final String deathStage; + private final ExistenceForm existenceForm; + private final int deathDelay; + + public DeathCondition(Requirement[] requirements, String deathStage, ExistenceForm existenceForm, int deathDelay) { + this.requirements = requirements; + this.deathStage = deathStage; + this.existenceForm = existenceForm; + this.deathDelay = deathDelay; + } + + @Nullable + public String deathStage() { + return deathStage; + } + + public int deathDelay() { + return deathDelay; + } + + public boolean isMet(Context context) { + return RequirementManager.isSatisfied(context, requirements); + } + + public ExistenceForm existenceForm() { + return existenceForm; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java new file mode 100644 index 000000000..b0dbc8ebb --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java @@ -0,0 +1,96 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; +import net.momirealms.customcrops.api.core.ConfigManager; +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.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; +import net.momirealms.customcrops.api.event.GreenhouseGlassBreakEvent; +import net.momirealms.customcrops.api.event.GreenhouseGlassInteractEvent; +import net.momirealms.customcrops.api.event.GreenhouseGlassPlaceEvent; +import net.momirealms.customcrops.api.util.EventUtils; + +import java.util.Optional; + +public class GreenhouseBlock extends AbstractCustomCropsBlock { + + public GreenhouseBlock() { + super(BuiltInBlockMechanics.GREENHOUSE.key()); + } + + @Override + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + //tickGreenhouse(world, location); + } + + @Override + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + tickGreenhouse(world, location); + } + + private void tickGreenhouse(CustomCropsWorld world, Pos3 location) { + if (!ConfigManager.doubleCheck()) return; + String id = BukkitCustomCropsPlugin.getInstance().getItemManager().id(location.toLocation(world.bukkitWorld()), ConfigManager.greenhouseExistenceForm()); + if (ConfigManager.greenhouse().contains(id)) return; + // remove outdated data + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Greenhouse is removed at location[" + world.worldName() + "," + location + "] because the id of the block/furniture is [" + id + "]"); + world.removeBlockState(location); + } + + @Override + public void onInteract(WrappedInteractEvent event) { + CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(event.location()); + CustomCropsBlockState state = getOrFixState(world, pos3); + GreenhouseGlassInteractEvent interactEvent = new GreenhouseGlassInteractEvent(event.player(), event.itemInHand(), event.location(), event.relatedID(), state, event.hand()); + EventUtils.fireAndForget(interactEvent); + } + + @Override + public void onBreak(WrappedBreakEvent event) { + CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(event.location()); + CustomCropsBlockState state = getOrFixState(world, pos3); + GreenhouseGlassBreakEvent breakEvent = new GreenhouseGlassBreakEvent(event.entityBreaker(), event.blockBreaker(), event.location(), event.brokenID(), state, event.reason()); + if (EventUtils.fireAndCheckCancel(breakEvent)) { + return; + } + world.removeBlockState(pos3); + } + + @Override + public void onPlace(WrappedPlaceEvent event) { + CustomCropsBlockState state = createBlockState(); + GreenhouseGlassPlaceEvent placeEvent = new GreenhouseGlassPlaceEvent(event.player(), event.location(), event.itemID(), state); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + return; + } + Pos3 pos3 = Pos3.from(event.location()); + CustomCropsWorld world = event.world(); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + } + + public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) { + Optional optional = world.getBlockState(pos3); + if (optional.isPresent() && optional.get().type() instanceof GreenhouseBlock) { + return optional.get(); + } + CustomCropsBlockState state = createBlockState(); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + return state; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java new file mode 100644 index 000000000..b8f856150 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java @@ -0,0 +1,25 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.api.requirement.RequirementManager; + +public class GrowCondition { + + private final Requirement[] requirements; + private final int pointToAdd; + + public GrowCondition(Requirement[] requirements, int pointToAdd) { + this.requirements = requirements; + this.pointToAdd = pointToAdd; + } + + public int pointToAdd() { + return pointToAdd; + } + + public boolean isMet(Context context) { + return RequirementManager.isSatisfied(context, requirements); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java new file mode 100644 index 000000000..008069140 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -0,0 +1,574 @@ +package net.momirealms.customcrops.api.core.block; + +import com.flowpowered.nbt.*; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.item.Fertilizer; +import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.water.WateringMethod; +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.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; +import net.momirealms.customcrops.api.event.*; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.PlayerUtils; +import net.momirealms.customcrops.api.util.StringUtils; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.block.data.type.Farmland; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class PotBlock extends AbstractCustomCropsBlock { + + public PotBlock() { + super(BuiltInBlockMechanics.POT.key()); + } + + @Override + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + if (!world.setting().randomTickPot() && canTick(state, world.setting().tickPotInterval())) { + tickPot(state, world, location); + } + } + + @Override + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + if (world.setting().randomTickPot() && canTick(state, world.setting().tickPotInterval())) { + tickPot(state, world, location); + } + } + + @Override + public void onBreak(WrappedBreakEvent event) { + CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(event.location()); + PotConfig config = Registries.ITEM_TO_POT.get(event.brokenID()); + if (config == null) { + world.removeBlockState(pos3); + return; + } + + final Player player = event.playerBreaker(); + Context context = Context.player(player); + if (!RequirementManager.isSatisfied(context, config.breakRequirements())) { + event.setCancelled(true); + return; + } + + Location upperLocation = event.location().clone().add(0,1,0); + String upperID = BukkitCustomCropsPlugin.getInstance().getItemManager().anyID(upperLocation); + List cropConfigs = Registries.STAGE_TO_CROP_UNSAFE.get(upperID); + + CropConfig cropConfig = null; + CropStageConfig stageConfig = null; + + outer: { + if (cropConfigs != null && !cropConfigs.isEmpty()) { + CropBlock cropBlock = (CropBlock) BuiltInBlockMechanics.CROP.mechanic(); + CustomCropsBlockState state = cropBlock.fixOrGetState(world, pos3.add(0,1,0), upperID); + if (state == null) { + // remove ambiguous stage + BukkitCustomCropsPlugin.getInstance().getItemManager().remove(upperLocation, ExistenceForm.ANY); + break outer; + } + + cropConfig = cropBlock.config(state); + if (cropConfig == null || !cropConfigs.contains(cropConfig)) { + if (cropConfigs.size() != 1) { + // remove ambiguous stage + BukkitCustomCropsPlugin.getInstance().getItemManager().remove(upperLocation, ExistenceForm.ANY); + world.removeBlockState(pos3.add(0,1,0)); + break outer; + } + cropConfig = cropConfigs.get(0); + } + + if (!RequirementManager.isSatisfied(context, cropConfig.breakRequirements())) { + event.setCancelled(true); + return; + } + stageConfig = cropConfig.stageByID(upperID); + // should not be null + assert stageConfig != null; + if (!RequirementManager.isSatisfied(context, stageConfig.breakRequirements())) { + event.setCancelled(true); + return; + } + + CropBreakEvent breakEvent = new CropBreakEvent(event.entityBreaker(), event.blockBreaker(), cropConfig, upperID, upperLocation, state, event.reason()); + if (EventUtils.fireAndCheckCancel(breakEvent)) { + event.setCancelled(true); + return; + } + } + } + + CustomCropsBlockState potState = fixOrGetState(world, pos3, config, event.brokenID()); + + PotBreakEvent breakEvent = new PotBreakEvent(event.entityBreaker(), event.blockBreaker(), event.location(), config, potState, event.reason()); + if (EventUtils.fireAndCheckCancel(breakEvent)) { + event.setCancelled(true); + return; + } + + ActionManager.trigger(context, config.breakActions()); + if (stageConfig != null) { + ActionManager.trigger(context, stageConfig.breakActions()); + } + if (cropConfig != null) { + ActionManager.trigger(context, cropConfig.breakActions()); + world.removeBlockState(pos3.add(0,1,0)); + BukkitCustomCropsPlugin.getInstance().getItemManager().remove(upperLocation, ExistenceForm.ANY); + } + + world.removeBlockState(pos3); + } + + @Override + public void onPlace(WrappedPlaceEvent event) { + PotConfig config = Registries.ITEM_TO_POT.get(event.placedID()); + if (config == null) { + event.setCancelled(true); + return; + } + + Context context = Context.player(event.player()); + if (!RequirementManager.isSatisfied(context, config.placeRequirements())) { + event.setCancelled(true); + return; + } + + CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(event.location()); + if (world.setting().potPerChunk() >= 0) { + if (world.testChunkLimitation(pos3, this.getClass(), world.setting().potPerChunk())) { + event.setCancelled(true); + ActionManager.trigger(context, config.reachLimitActions()); + return; + } + } + + CustomCropsBlockState state = BuiltInBlockMechanics.POT.createBlockState(); + id(state, config.id()); + water(state, config.isWet(event.placedID()) ? 1 : 0); + + PotPlaceEvent placeEvent = new PotPlaceEvent(event.player(), event.location(), config, state, event.item(), event.hand()); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + event.setCancelled(true); + return; + } + + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + ActionManager.trigger(context, config.placeActions()); + } + + @Override + public void onInteract(WrappedInteractEvent event) { + PotConfig potConfig = Registries.ITEM_TO_POT.get(event.relatedID()); + if (potConfig == null) { + return; + } + + Location location = event.location(); + Pos3 pos3 = Pos3.from(location); + CustomCropsWorld world = event.world(); + + // fix or get data + CustomCropsBlockState state = fixOrGetState(world, pos3, potConfig, event.relatedID()); + + final Player player = event.player(); + Context context = Context.player(player); + // check use requirements + if (!RequirementManager.isSatisfied(context, potConfig.useRequirements())) { + return; + } + + final ItemStack itemInHand = event.itemInHand(); + // trigger event + PotInteractEvent interactEvent = new PotInteractEvent(player, event.hand(), itemInHand, potConfig, location, state); + if (EventUtils.fireAndCheckCancel(interactEvent)) { + return; + } + + if (tryWateringPot(player, context, state, event.hand(), event.itemID(), potConfig, location, itemInHand)) + return; + + ActionManager.trigger(context, potConfig.interactActions()); + } + + protected boolean tryWateringPot(Player player, Context context, CustomCropsBlockState state, EquipmentSlot hand, String itemID, PotConfig potConfig, Location potLocation, ItemStack itemInHand) { + int waterInPot = water(state); + for (WateringMethod method : potConfig.wateringMethods()) { + if (method.getUsed().equals(itemID) && method.getUsedAmount() <= itemInHand.getAmount()) { + if (method.checkRequirements(context)) { + if (waterInPot >= potConfig.storage()) { + ActionManager.trigger(context, potConfig.fullWaterActions()); + } else { + PotFillEvent waterEvent = new PotFillEvent(player, itemInHand, hand, potLocation, method, state, potConfig); + if (EventUtils.fireAndCheckCancel(waterEvent)) + return true; + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(Math.max(0, itemInHand.getAmount() - method.getUsedAmount())); + if (method.getReturned() != null) { + ItemStack returned = BukkitCustomCropsPlugin.getInstance().getItemManager().build(player, method.getReturned()); + if (returned != null) { + PlayerUtils.giveItem(player, returned, method.getReturnedAmount()); + } + } + } + method.triggerActions(context); + ActionManager.trigger(context, potConfig.addWaterActions()); + } + } + return true; + } + } + return false; + } + + public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, PotConfig potConfig, String blockID) { + Optional optionalPotState = world.getBlockState(pos3); + if (optionalPotState.isPresent()) { + CustomCropsBlockState potState = optionalPotState.get(); + if (potState.type() instanceof PotBlock potBlock) { + if (potBlock.id(potState).equals(potConfig.id())) { + return potState; + } + } + } + CustomCropsBlockState state = BuiltInBlockMechanics.POT.createBlockState(); + id(state, potConfig.id()); + water(state, potConfig.isWet(blockID) ? 1 : 0); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + return state; + } + + private void tickPot(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + PotConfig config = config(state); + if (config == null) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Pot data is removed at location[" + world.worldName() + "," + location + "] because the pot config[" + id(state) + "] has been removed."); + world.removeBlockState(location); + return; + } + + World bukkitWorld = world.bukkitWorld(); + if (ConfigManager.doubleCheck()) { + String blockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(location.toLocation(bukkitWorld)); + if (!config.blocks().contains(blockID)) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Pot[" + config.id() + "] is removed at location[" + world.worldName() + "," + location + "] because the id of the block is [" + blockID + "]"); + world.removeBlockState(location); + return; + } + } + + boolean hasNaturalWater = false; + boolean waterChanged = false; + + if (config.isRainDropAccepted()) { + if (bukkitWorld.hasStorm() || (!bukkitWorld.isClearWeather() && !bukkitWorld.isThundering())) { + double temperature = bukkitWorld.getTemperature(location.x(), location.y(), location.z()); + if (temperature > 0.15 && temperature < 0.85) { + int y = bukkitWorld.getHighestBlockYAt(location.x(), location.z()); + if (y == location.y()) { + if (addWater(state, 1)) { + waterChanged = true; + } + hasNaturalWater = true; + } + } + } + } + + if (!hasNaturalWater && config.isNearbyWaterAccepted()) { + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + for (int k : new int[]{0, 1}) { + BlockData block = bukkitWorld.getBlockData(location.x() + i, location.y() + j, location.z() + k); + if (block.getMaterial() == Material.WATER || (block instanceof Waterlogged waterlogged && waterlogged.isWaterlogged())) { + if (addWater(state, 1)) { + waterChanged = true; + } + hasNaturalWater = true; + } + } + } + } + } + + if (!hasNaturalWater) { + int waterToLose = 1; + Fertilizer[] fertilizers = fertilizers(state); + for (Fertilizer fertilizer : fertilizers) { + FertilizerConfig fertilizerConfig = fertilizer.config(); + if (fertilizerConfig != null) { + waterToLose = fertilizerConfig.processWaterToLose(waterToLose); + } + } + if (waterToLose > 0) { + if (addWater(state, -waterToLose)) { + waterChanged = true; + } + } + } + + boolean fertilizerChanged = tickFertilizer(state); + + if (fertilizerChanged || waterChanged) { + updateBlockAppearance(location.toLocation(bukkitWorld), config, hasNaturalWater, fertilizers(state)); + } + } + + public int water(CustomCropsBlockState state) { + Tag tag = state.get("water"); + if (tag == null) { + return 0; + } + return tag.getAsIntTag().map(IntTag::getValue).orElse(0); + } + + public boolean addWater(CustomCropsBlockState state, int water) { + return water(state, water + water(state)); + } + + public boolean addWater(CustomCropsBlockState state, PotConfig config, int water) { + return water(state, config, water + water(state)); + } + + public boolean consumeWater(CustomCropsBlockState state, int water) { + return water(state, water(state) - water); + } + + public boolean consumeWater(CustomCropsBlockState state, PotConfig config, int water) { + return water(state, config, water(state) - water); + } + + public boolean water(CustomCropsBlockState state, int water) { + return water(state, config(state), water); + } + + /** + * Set the water for a pot + * + * @param state the block state + * @param config the pot config + * @param water the amount of water + * @return whether the moisture state has been changed + */ + public boolean water(CustomCropsBlockState state, PotConfig config, int water) { + if (water < 0) water = 0; + int current = Math.min(water, config.storage()); + int previous = water(state); + if (water == previous) return false; + state.set("water", new IntTag("water", current)); + return previous == 0 ^ current == 0; + } + + public PotConfig config(CustomCropsBlockState state) { + return Registries.POT.get(id(state)); + } + + /** + * Get the fertilizers in the pot + * + * @param state the block state + * @return applied fertilizers + */ + @SuppressWarnings("unchecked") + @NotNull + public Fertilizer[] fertilizers(CustomCropsBlockState state) { + Tag fertilizerTag = state.get("fertilizers"); + if (fertilizerTag == null) return new Fertilizer[0]; + List tags = ((ListTag) fertilizerTag.getValue()).getValue(); + Fertilizer[] fertilizers = new Fertilizer[tags.size()]; + for (int i = 0; i < tags.size(); i++) { + CompoundTag tag = tags.get(i); + fertilizers[i] = tagToFertilizer(tag.getValue()); + } + return fertilizers; + } + + /** + * Check if the fertilizer can be applied to this pot + * + * @param state the block state + * @param fertilizer the fertilizer to apply + * @return can be applied or not + */ + public boolean canApplyFertilizer(CustomCropsBlockState state, Fertilizer fertilizer) { + Fertilizer[] fertilizers = fertilizers(state); + boolean hasSameTypeFertilizer = false; + for (Fertilizer applied : fertilizers) { + if (fertilizer.id().equals(applied.id())) { + return true; + } + if (fertilizer.type() == applied.type()) { + return false; + } + } + PotConfig config = config(state); + return config.maxFertilizers() > fertilizers.length; + } + + /** + * Add fertilizer to the pot + * If the pot contains the fertilizer, the times would be reset. + * + * @param state the block state + * @param fertilizer the fertilizer to apply + * @return whether to update pot appearance + */ + @SuppressWarnings("unchecked") + public boolean addFertilizer(CustomCropsBlockState state, Fertilizer fertilizer) { + Tag fertilizerTag = state.get("fertilizers"); + if (fertilizerTag == null) { + fertilizerTag = new ListTag("", TagType.TAG_COMPOUND, new ArrayList<>()); + state.set("fertilizers", fertilizerTag); + } + List tags = ((ListTag) fertilizerTag.getValue()).getValue(); + for (CompoundTag tag : tags) { + CompoundMap map = tag.getValue(); + Fertilizer applied = tagToFertilizer(map); + if (fertilizer.id().equals(applied.id())) { + map.put(new IntTag("times", fertilizer.times())); + return false; + } + if (fertilizer.type() == applied.type()) { + return false; + } + } + PotConfig config = config(state); + if (config.maxFertilizers() <= tags.size()) { + return false; + } + tags.add(new CompoundTag("", fertilizerToTag(fertilizer))); + return true; + } + + @SuppressWarnings("unchecked") + private boolean tickFertilizer(CustomCropsBlockState state) { + // no fertilizers applied + Tag fertilizerTag = state.get("fertilizers"); + if (fertilizerTag == null) { + return false; + } + List tags = ((ListTag) fertilizerTag.getValue()).getValue(); + if (tags.isEmpty()) { + return false; + } + List fertilizerToRemove = new ArrayList<>(); + for (int i = 0; i < tags.size(); i++) { + CompoundMap map = tags.get(i).getValue(); + Fertilizer applied = tagToFertilizer(map); + if (applied.reduceTimes()) { + fertilizerToRemove.add(i); + } else { + tags.get(i).setValue(fertilizerToTag(applied)); + } + } + // no fertilizer is used up + if (fertilizerToRemove.isEmpty()) { + return false; + } + CompoundTag lastEntry = tags.get(tags.size() - 1); + Collections.reverse(fertilizerToRemove); + for (int i : fertilizerToRemove) { + tags.remove(i); + } + // all the fertilizers are used up + if (tags.isEmpty()) { + return true; + } + // if the most recent applied fertilizer is the same + CompoundTag newLastEntry = tags.get(tags.size() - 1); + return lastEntry != newLastEntry; + } + + public void updateBlockAppearance(Location location, CustomCropsBlockState state) { + updateBlockAppearance(location, state, fertilizers(state)); + } + + public void updateBlockAppearance(Location location, CustomCropsBlockState state, Fertilizer[] fertilizers) { + updateBlockAppearance(location, state, water(state) != 0, fertilizers); + } + + public void updateBlockAppearance(Location location, CustomCropsBlockState state, boolean hasWater, Fertilizer[] fertilizers) { + updateBlockAppearance( + location, + config(state), + hasWater, + // always using the latest fertilizer as appearance + fertilizers.length == 0 ? null : fertilizers[fertilizers.length - 1] + ); + } + + public void updateBlockAppearance(Location location, PotConfig config, boolean hasWater, Fertilizer[] fertilizers) { + updateBlockAppearance( + location, + config, + hasWater, + // always using the latest fertilizer as appearance + fertilizers.length == 0 ? null : fertilizers[fertilizers.length - 1] + ); + } + + public void updateBlockAppearance(Location location, PotConfig config, boolean hasWater, @Nullable Fertilizer fertilizer) { + String appearance = config.getPotAppearance(hasWater, fertilizer == null ? null : fertilizer.type()); + if (StringUtils.isCapitalLetter(appearance)) { + Block block = location.getBlock(); + Material type = Material.valueOf(appearance); + if (type == Material.FARMLAND) { + Farmland data = ((Farmland) Material.FARMLAND.createBlockData()); + data.setMoisture(hasWater ? 7 : 0); + block.setBlockData(data, false); + } else { + block.setType(type, false); + } + } else { + BukkitCustomCropsPlugin.getInstance().getItemManager().place(location, ExistenceForm.BLOCK, appearance, FurnitureRotation.NONE); + } + } + + private Fertilizer tagToFertilizer(CompoundMap tag) { + return Fertilizer.builder() + .id(((StringTag) tag.get("id")).getValue()) + .times(((IntTag) tag.get("times")).getValue()) + .build(); + } + + private CompoundMap fertilizerToTag(Fertilizer fertilizer) { + CompoundMap tag = new CompoundMap(); + tag.put(new IntTag("times", fertilizer.times())); + tag.put(new StringTag("id", fertilizer.id())); + return tag; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java new file mode 100644 index 000000000..7ec309e29 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java @@ -0,0 +1,103 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Set; + +public interface PotConfig { + + String id(); + + int storage(); + + boolean isRainDropAccepted(); + + boolean isNearbyWaterAccepted(); + + WateringMethod[] wateringMethods(); + + Set blocks(); + + boolean isWet(String blockID); + + WaterBar waterBar(); + + int maxFertilizers(); + + String getPotAppearance(boolean watered, FertilizerType type); + + Requirement[] placeRequirements(); + + Requirement[] breakRequirements(); + + Requirement[] useRequirements(); + + Action[] tickActions(); + + Action[] reachLimitActions(); + + Action[] interactActions(); + + Action[] placeActions(); + + Action[] breakActions(); + + Action[] addWaterActions(); + + Action[] fullWaterActions(); + + static Builder builder() { + return new PotConfigImpl.BuilderImpl(); + } + + interface Builder { + + PotConfig build(); + + Builder id(String id); + + Builder storage(int storage); + + Builder isRainDropAccepted(boolean isRainDropAccepted); + + Builder isNearbyWaterAccepted(boolean isNearbyWaterAccepted); + + Builder wateringMethods(WateringMethod[] wateringMethods); + + Builder waterBar(WaterBar waterBar); + + Builder maxFertilizers(int maxFertilizers); + + Builder placeRequirements(Requirement[] requirements); + + Builder breakRequirements(Requirement[] requirements); + + Builder useRequirements(Requirement[] requirements); + + Builder tickActions(Action[] tickActions); + + Builder reachLimitActions(Action[] reachLimitActions); + + Builder interactActions(Action[] interactActions); + + Builder placeActions(Action[] placeActions); + + Builder breakActions(Action[] breakActions); + + Builder addWaterActions(Action[] addWaterActions); + + Builder fullWaterActions(Action[] fullWaterActions); + + Builder basicAppearance(Pair basicAppearance); + + Builder potAppearanceMap(HashMap> potAppearanceMap); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java new file mode 100644 index 000000000..18b115e45 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java @@ -0,0 +1,339 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +public class PotConfigImpl implements PotConfig { + + private final String id; + private final Pair basicAppearance; + private final HashMap> potAppearanceMap; + private final Set blocks = new HashSet<>(); + private final Set wetBlocks = new HashSet<>(); + private final int storage; + private final boolean isRainDropAccepted; + private final boolean isNearbyWaterAccepted; + private final WateringMethod[] wateringMethods; + private final WaterBar waterBar; + private final int maxFertilizers; + private final Requirement[] placeRequirements; + private final Requirement[] breakRequirements; + private final Requirement[] useRequirements; + private final Action[] tickActions; + private final Action[] reachLimitActions; + private final Action[] interactActions; + private final Action[] placeActions; + private final Action[] breakActions; + private final Action[] addWaterActions; + private final Action[] fullWaterActions; + + public PotConfigImpl( + String id, + Pair basicAppearance, + HashMap> potAppearanceMap, + int storage, + boolean isRainDropAccepted, + boolean isNearbyWaterAccepted, + WateringMethod[] wateringMethods, + WaterBar waterBar, + int maxFertilizers, + Requirement[] placeRequirements, + Requirement[] breakRequirements, + Requirement[] useRequirements, + Action[] tickActions, + Action[] reachLimitActions, + Action[] interactActions, + Action[] placeActions, + Action[] breakActions, + Action[] addWaterActions, + Action[] fullWaterActions + ) { + this.id = id; + this.basicAppearance = basicAppearance; + this.potAppearanceMap = potAppearanceMap; + this.storage = storage; + this.isRainDropAccepted = isRainDropAccepted; + this.isNearbyWaterAccepted = isNearbyWaterAccepted; + this.wateringMethods = wateringMethods; + this.waterBar = waterBar; + this.maxFertilizers = maxFertilizers; + this.placeRequirements = placeRequirements; + this.breakRequirements = breakRequirements; + this.useRequirements = useRequirements; + this.tickActions = tickActions; + this.reachLimitActions = reachLimitActions; + this.interactActions = interactActions; + this.placeActions = placeActions; + this.breakActions = breakActions; + this.addWaterActions = addWaterActions; + this.fullWaterActions = fullWaterActions; + this.blocks.add(basicAppearance.left()); + this.blocks.add(basicAppearance.right()); + this.wetBlocks.add(basicAppearance.right()); + for (Pair pair : potAppearanceMap.values()) { + this.blocks.add(pair.left()); + this.blocks.add(pair.right()); + this.wetBlocks.add(pair.right()); + } + } + + @Override + public String id() { + return id; + } + + @Override + public int storage() { + return storage; + } + + @Override + public boolean isRainDropAccepted() { + return isRainDropAccepted; + } + + @Override + public boolean isNearbyWaterAccepted() { + return isNearbyWaterAccepted; + } + + @Override + public WateringMethod[] wateringMethods() { + return wateringMethods; + } + + @Override + public Set blocks() { + return blocks; + } + + @Override + public boolean isWet(String blockID) { + return wetBlocks.contains(blockID); + } + + @Override + public WaterBar waterBar() { + return waterBar; + } + + @Override + public int maxFertilizers() { + return maxFertilizers; + } + + @Override + public String getPotAppearance(boolean watered, FertilizerType type) { + if (type != null) { + Pair appearance = potAppearanceMap.get(type); + if (appearance != null) { + return watered ? appearance.right() : appearance.left(); + } + } + return watered ? basicAppearance.right() : basicAppearance.left(); + } + + @Override + public Requirement[] placeRequirements() { + return placeRequirements; + } + + @Override + public Requirement[] breakRequirements() { + return breakRequirements; + } + + @Override + public Requirement[] useRequirements() { + return useRequirements; + } + + @Override + public Action[] tickActions() { + return tickActions; + } + + @Override + public Action[] reachLimitActions() { + return reachLimitActions; + } + + @Override + public Action[] interactActions() { + return interactActions; + } + + @Override + public Action[] placeActions() { + return placeActions; + } + + @Override + public Action[] breakActions() { + return breakActions; + } + + @Override + public Action[] addWaterActions() { + return addWaterActions; + } + + @Override + public Action[] fullWaterActions() { + return fullWaterActions; + } + + public static class BuilderImpl implements Builder { + + private String id; + private ExistenceForm existenceForm; + private Pair basicAppearance; + private HashMap> potAppearanceMap; + private int storage; + private boolean isRainDropAccepted; + private boolean isNearbyWaterAccepted; + private WateringMethod[] wateringMethods; + private WaterBar waterBar; + private int maxFertilizers; + private Requirement[] placeRequirements; + private Requirement[] breakRequirements; + private Requirement[] useRequirements; + private Action[] tickActions; + private Action[] reachLimitActions; + private Action[] interactActions; + private Action[] placeActions; + private Action[] breakActions; + private Action[] addWaterActions; + private Action[] fullWaterActions; + + @Override + public PotConfig build() { + return new PotConfigImpl(id, basicAppearance, potAppearanceMap, storage, isRainDropAccepted, isNearbyWaterAccepted, wateringMethods, waterBar, maxFertilizers, placeRequirements, breakRequirements, useRequirements, tickActions, reachLimitActions, interactActions, placeActions, breakActions, addWaterActions, fullWaterActions); + } + + @Override + public Builder id(String id) { + this.id = id; + return this; + } + + @Override + public Builder storage(int storage) { + this.storage = storage; + return this; + } + + @Override + public Builder isRainDropAccepted(boolean isRainDropAccepted) { + this.isRainDropAccepted = isRainDropAccepted; + return this; + } + + @Override + public Builder isNearbyWaterAccepted(boolean isNearbyWaterAccepted) { + this.isNearbyWaterAccepted = isNearbyWaterAccepted; + return this; + } + + @Override + public Builder wateringMethods(WateringMethod[] wateringMethods) { + this.wateringMethods = wateringMethods; + return this; + } + + @Override + public Builder waterBar(WaterBar waterBar) { + this.waterBar = waterBar; + return this; + } + + @Override + public Builder maxFertilizers(int maxFertilizers) { + this.maxFertilizers = maxFertilizers; + return this; + } + + @Override + public Builder placeRequirements(Requirement[] requirements) { + this.placeRequirements = requirements; + return this; + } + + @Override + public Builder breakRequirements(Requirement[] requirements) { + this.breakRequirements = requirements; + return this; + } + + @Override + public Builder useRequirements(Requirement[] requirements) { + this.useRequirements = requirements; + return this; + } + + @Override + public Builder tickActions(Action[] tickActions) { + this.tickActions = tickActions; + return this; + } + + @Override + public Builder reachLimitActions(Action[] reachLimitActions) { + this.reachLimitActions = reachLimitActions; + return this; + } + + @Override + public Builder interactActions(Action[] interactActions) { + this.interactActions = interactActions; + return this; + } + + @Override + public Builder placeActions(Action[] placeActions) { + this.placeActions = placeActions; + return this; + } + + @Override + public Builder breakActions(Action[] breakActions) { + this.breakActions = breakActions; + return this; + } + + @Override + public Builder addWaterActions(Action[] addWaterActions) { + this.addWaterActions = addWaterActions; + return this; + } + + @Override + public Builder fullWaterActions(Action[] fullWaterActions) { + this.fullWaterActions = fullWaterActions; + return this; + } + + @Override + public Builder basicAppearance(Pair basicAppearance) { + this.basicAppearance = basicAppearance; + return this; + } + + @Override + public Builder potAppearanceMap(HashMap> potAppearanceMap) { + this.potAppearanceMap = potAppearanceMap; + return this; + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java new file mode 100644 index 000000000..18808248b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java @@ -0,0 +1,94 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; +import net.momirealms.customcrops.api.core.ConfigManager; +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.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; +import net.momirealms.customcrops.api.event.*; +import net.momirealms.customcrops.api.util.EventUtils; + +import java.util.Optional; + +public class ScarecrowBlock extends AbstractCustomCropsBlock { + + public ScarecrowBlock() { + super(BuiltInBlockMechanics.SCARECROW.key()); + } + + @Override + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + //tickScarecrow(world, location); + } + + @Override + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + tickScarecrow(world, location); + } + + private void tickScarecrow(CustomCropsWorld world, Pos3 location) { + if (!ConfigManager.doubleCheck()) return; + String id = BukkitCustomCropsPlugin.getInstance().getItemManager().id(location.toLocation(world.bukkitWorld()), ConfigManager.scarecrowExistenceForm()); + if (ConfigManager.scarecrow().contains(id)) return; + // remove outdated data + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Scarecrow is removed at location[" + world.worldName() + "," + location + "] because the id of the block/furniture is [" + id + "]"); + world.removeBlockState(location); + } + + @Override + public void onInteract(WrappedInteractEvent event) { + CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(event.location()); + CustomCropsBlockState state = getOrFixState(world, pos3); + ScarecrowInteractEvent interactEvent = new ScarecrowInteractEvent(event.player(), event.itemInHand(), event.location(), event.relatedID(), state, event.hand()); + EventUtils.fireAndForget(interactEvent); + } + + @Override + public void onBreak(WrappedBreakEvent event) { + CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(event.location()); + CustomCropsBlockState state = getOrFixState(world, pos3); + ScarecrowBreakEvent breakEvent = new ScarecrowBreakEvent(event.entityBreaker(), event.blockBreaker(), event.location(), event.brokenID(), state, event.reason()); + if (EventUtils.fireAndCheckCancel(breakEvent)) { + return; + } + world.removeBlockState(pos3); + } + + @Override + public void onPlace(WrappedPlaceEvent event) { + CustomCropsBlockState state = createBlockState(); + ScarecrowPlaceEvent placeEvent = new ScarecrowPlaceEvent(event.player(), event.location(), event.itemID(), state); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + return; + } + Pos3 pos3 = Pos3.from(event.location()); + CustomCropsWorld world = event.world(); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + } + + public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) { + Optional optional = world.getBlockState(pos3); + if (optional.isPresent() && optional.get().type() instanceof ScarecrowBlock) { + return optional.get(); + } + CustomCropsBlockState state = createBlockState(); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + return state; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java new file mode 100644 index 000000000..3b6373a82 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -0,0 +1,310 @@ +package net.momirealms.customcrops.api.core.block; + +import com.flowpowered.nbt.IntTag; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.water.WateringMethod; +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.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; +import net.momirealms.customcrops.api.event.SprinklerBreakEvent; +import net.momirealms.customcrops.api.event.SprinklerFillEvent; +import net.momirealms.customcrops.api.event.SprinklerInteractEvent; +import net.momirealms.customcrops.api.event.SprinklerPlaceEvent; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.PlayerUtils; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +public class SprinklerBlock extends AbstractCustomCropsBlock { + + public SprinklerBlock() { + super(BuiltInBlockMechanics.SPRINKLER.key()); + } + + @Override + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + if (!world.setting().randomTickSprinkler() && canTick(state, world.setting().tickSprinklerInterval())) { + tickSprinkler(state, world, location); + } + } + + @Override + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + if (world.setting().randomTickSprinkler() && canTick(state, world.setting().tickSprinklerInterval())) { + tickSprinkler(state, world, location); + } + } + + @Override + public void onBreak(WrappedBreakEvent event) { + CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(event.location()); + SprinklerConfig config = Registries.ITEM_TO_SPRINKLER.get(event.brokenID()); + if (config == null) { + world.removeBlockState(pos3); + return; + } + + final Player player = event.playerBreaker(); + Context context = Context.player(player); + CustomCropsBlockState state = fixOrGetState(world, pos3, config, event.brokenID()); + if (!RequirementManager.isSatisfied(context, config.breakRequirements())) { + event.setCancelled(true); + return; + } + + SprinklerBreakEvent breakEvent = new SprinklerBreakEvent(event.entityBreaker(), event.blockBreaker(), event.location(), state, config, event.reason()); + if (EventUtils.fireAndCheckCancel(breakEvent)) { + event.setCancelled(true); + return; + } + + world.removeBlockState(pos3); + ActionManager.trigger(context, config.breakActions()); + } + + @Override + public void onPlace(WrappedPlaceEvent event) { + SprinklerConfig config = Registries.ITEM_TO_SPRINKLER.get(event.placedID()); + if (config == null) { + event.setCancelled(true); + return; + } + + final Player player = event.player(); + Context context = Context.player(player); + if (!RequirementManager.isSatisfied(context, config.placeRequirements())) { + event.setCancelled(true); + return; + } + + Pos3 pos3 = Pos3.from(event.location()); + CustomCropsWorld world = event.world(); + if (world.setting().sprinklerPerChunk() >= 0) { + if (world.testChunkLimitation(pos3, this.getClass(), world.setting().sprinklerPerChunk())) { + event.setCancelled(true); + ActionManager.trigger(context, config.reachLimitActions()); + return; + } + } + + CustomCropsBlockState state = createBlockState(); + id(state, config.id()); + water(state, config.threeDItemWithWater().equals(event.placedID()) ? 1 : 0); + + SprinklerPlaceEvent placeEvent = new SprinklerPlaceEvent(player, event.item(), event.hand(), event.location(), config, state); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + event.setCancelled(true); + return; + } + + world.addBlockState(pos3, state); + ActionManager.trigger(context, config.placeActions()); + } + + @Override + public void onInteract(WrappedInteractEvent event) { + SprinklerConfig config = Registries.ITEM_TO_SPRINKLER.get(event.relatedID()); + if (config == null) { + return; + } + + final Player player = event.player(); + Context context = Context.player(player); + CustomCropsBlockState state = fixOrGetState(event.world(), Pos3.from(event.location()), config, event.relatedID()); + if (!RequirementManager.isSatisfied(context, config.useRequirements())) { + return; + } + + int waterInSprinkler = water(state); + String itemID = event.itemID(); + ItemStack itemInHand = event.itemInHand(); + if (!config.infinite()) { + for (WateringMethod method : config.wateringMethods()) { + if (method.getUsed().equals(itemID) && method.getUsedAmount() <= itemInHand.getAmount()) { + if (method.checkRequirements(context)) { + if (waterInSprinkler >= config.storage()) { + ActionManager.trigger(context, config.fullWaterActions()); + } else { + SprinklerFillEvent waterEvent = new SprinklerFillEvent(player, itemInHand, event.hand(), event.location(), method, state, config); + if (EventUtils.fireAndCheckCancel(waterEvent)) + return; + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(Math.max(0, itemInHand.getAmount() - method.getUsedAmount())); + if (method.getReturned() != null) { + ItemStack returned = BukkitCustomCropsPlugin.getInstance().getItemManager().build(player, method.getReturned()); + if (returned != null) { + PlayerUtils.giveItem(player, returned, method.getReturnedAmount()); + } + } + } + method.triggerActions(context); + ActionManager.trigger(context, config.addWaterActions()); + } + } + return; + } + } + } + + SprinklerInteractEvent interactEvent = new SprinklerInteractEvent(player, event.itemInHand(), event.location(), config, state, event.hand()); + if (EventUtils.fireAndCheckCancel(interactEvent)) { + return; + } + + ActionManager.trigger(context, config.interactActions()); + } + + public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, SprinklerConfig sprinklerConfig, String blockID) { + Optional optionalPotState = world.getBlockState(pos3); + if (optionalPotState.isPresent()) { + CustomCropsBlockState potState = optionalPotState.get(); + if (potState.type() instanceof SprinklerBlock sprinklerBlock) { + if (sprinklerBlock.id(potState).equals(sprinklerConfig.id())) { + return potState; + } + } + } + CustomCropsBlockState state = BuiltInBlockMechanics.SPRINKLER.createBlockState(); + id(state, sprinklerConfig.id()); + water(state, blockID.equals(sprinklerConfig.threeDItemWithWater()) ? 1 : 0); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + return state; + } + + private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + SprinklerConfig config = config(state); + if (config == null) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Sprinkler data is removed at location[" + world.worldName() + "," + location + "] because the sprinkler config[" + id(state) + "] has been removed."); + world.removeBlockState(location); + return; + } + boolean updateState; + if (!config.infinite()) { + int water = water(state); + if (water <= 0) { + return; + } + water(state, --water); + updateState = water == 0; + } else { + updateState = false; + } + + Context context = Context.block(state); + World bukkitWorld = world.bukkitWorld(); + Location bukkitLocation = location.toLocation(bukkitWorld); + + CompletableFuture syncCheck = new CompletableFuture<>(); + + // place/remove entities on main thread + BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run(() -> { + + if (ConfigManager.doubleCheck()) { + String modelID = BukkitCustomCropsPlugin.getInstance().getItemManager().id(bukkitLocation, config.existenceForm()); + if (modelID == null || !config.modelIDs().contains(modelID)) { + world.removeBlockState(location); + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Sprinkler[" + config.id() + "] is removed at Location[" + world.worldName() + "," + location + "] because the id of the block/furniture is " + modelID); + syncCheck.complete(false); + return; + } + } + + ActionManager.trigger(context, config.workActions()); + if (updateState && !config.threeDItem().equals(config.threeDItemWithWater())) { + updateBlockAppearance(bukkitLocation, config, false); + } + + syncCheck.complete(true); + }, bukkitLocation); + + syncCheck.thenAccept(result -> { + if (result) { + int[][] range = config.range(); + Pos3[] pos3s = new Pos3[range.length * 2]; + for (int i = 0; i < range.length; i++) { + int x = range[i][0]; + int z = range[i][1]; + pos3s[i] = location.add(x, 0, z); + pos3s[i] = location.add(x, -1, z); + } + + for (Pos3 pos3 : pos3s) { + Optional optionalState = world.getBlockState(pos3); + if (optionalState.isPresent()) { + CustomCropsBlockState anotherState = optionalState.get(); + if (anotherState.type() instanceof PotBlock potBlock) { + PotConfig potConfig = potBlock.config(anotherState); + if (config.potWhitelist().contains(potConfig.id())) { + if (potBlock.addWater(anotherState, potConfig, config.sprinklingAmount())) { + BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run( + () -> potBlock.updateBlockAppearance( + pos3.toLocation(world.bukkitWorld()), + potConfig, + true, + potBlock.fertilizers(anotherState) + ), + bukkitWorld, + pos3.chunkX(), pos3.chunkZ() + ); + } + } + } + } + } + } + }); + } + + public boolean addWater(CustomCropsBlockState state, int water) { + return water(state, water + water(state)); + } + + public boolean addWater(CustomCropsBlockState state, SprinklerConfig config, int water) { + return water(state, config, water + water(state)); + } + + public int water(CustomCropsBlockState state) { + return state.get("water").getAsIntTag().map(IntTag::getValue).orElse(0); + } + + public boolean water(CustomCropsBlockState state, int water) { + return water(state, config(state), water); + } + + public boolean water(CustomCropsBlockState state, SprinklerConfig config, int water) { + if (water < 0) water = 0; + int current = Math.min(water, config.storage()); + int previous = water(state); + if (water == previous) return false; + state.set("water", new IntTag("water", current)); + return previous == 0 ^ current == 0; + } + + public SprinklerConfig config(CustomCropsBlockState state) { + return Registries.SPRINKLER.get(id(state)); + } + + public void updateBlockAppearance(Location location, SprinklerConfig config, boolean hasWater) { + FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(location, ExistenceForm.ANY); + BukkitCustomCropsPlugin.getInstance().getItemManager().place(location, config.existenceForm(), hasWater ? config.threeDItemWithWater() : config.threeDItem(), rotation); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java new file mode 100644 index 000000000..c9a85b996 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java @@ -0,0 +1,148 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public interface SprinklerConfig { + + String id(); + + int storage(); + + int[][] range(); + + boolean infinite(); + + int sprinklingAmount(); + + @Nullable + String twoDItem(); + + @NotNull + String threeDItem(); + + @NotNull + String threeDItemWithWater(); + + @NotNull + Set potWhitelist(); + + @NotNull + Set modelIDs(); + + @Nullable + WaterBar waterBar(); + + @NotNull + ExistenceForm existenceForm(); + + /** + * Get the requirements for placement + * + * @return requirements for placement + */ + @Nullable + Requirement[] placeRequirements(); + + /** + * Get the requirements for breaking + * + * @return requirements for breaking + */ + @Nullable + Requirement[] breakRequirements(); + + /** + * Get the requirements for using + * + * @return requirements for using + */ + @Nullable + Requirement[] useRequirements(); + + @Nullable + Action[] workActions(); + + @Nullable + Action[] interactActions(); + + @Nullable + Action[] placeActions(); + + @Nullable + Action[] breakActions(); + + @Nullable + Action[] addWaterActions(); + + @Nullable + Action[] reachLimitActions(); + + @Nullable + Action[] fullWaterActions(); + + @NotNull + WateringMethod[] wateringMethods(); + + static Builder builder() { + return new SprinklerConfigImpl.BuilderImpl(); + } + + interface Builder { + + SprinklerConfig build(); + + Builder id(String id); + + Builder existenceForm(ExistenceForm existenceForm); + + Builder storage(int storage); + + Builder range(int[][] range); + + Builder infinite(boolean infinite); + + Builder sprinklingAmount(int sprinklingAmount); + + Builder potWhitelist(Set potWhitelist); + + Builder waterBar(WaterBar waterBar); + + Builder twoDItem(@Nullable String twoDItem); + + Builder threeDItem(String threeDItem); + + Builder threeDItemWithWater(String threeDItemWithWater); + + Builder placeRequirements(Requirement[] placeRequirements); + + Builder breakRequirements(Requirement[] breakRequirements); + + Builder useRequirements(Requirement[] useRequirements); + + Builder workActions(Action[] workActions); + + Builder interactActions(Action[] interactActions); + + Builder addWaterActions(Action[] addWaterActions); + + Builder reachLimitActions(Action[] reachLimitActions); + + Builder placeActions(Action[] placeActions); + + Builder breakActions(Action[] breakActions); + + Builder fullWaterActions(Action[] fullWaterActions); + + Builder wateringMethods(WateringMethod[] wateringMethods); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java new file mode 100644 index 000000000..3a3a52a18 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java @@ -0,0 +1,375 @@ +package net.momirealms.customcrops.api.core.block; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class SprinklerConfigImpl implements SprinklerConfig { + private final String id; + private final ExistenceForm existenceForm; + private final int storage; + private final int[][] range; + private final boolean infinite; + private final int sprinklingAmount; + private final Set potWhitelist; + private final WaterBar waterBar; + private final String twoDItem; + private final String threeDItem; + private final String threeDItemWithWater; + private final Requirement[] placeRequirements; + private final Requirement[] breakRequirements; + private final Requirement[] useRequirements; + private final Action[] workActions; + private final Action[] interactActions; + private final Action[] reachLimitActions; + private final Action[] addWaterActions; + private final Action[] placeActions; + private final Action[] breakActions; + private final Action[] fullWaterActions; + private final WateringMethod[] wateringMethods; + private final Set modelIDs = new HashSet<>(); + + public SprinklerConfigImpl( + String id, + ExistenceForm existenceForm, + int storage, + int[][] range, + boolean infinite, + int sprinklingAmount, + Set potWhitelist, + WaterBar waterBar, + String twoDItem, + String threeDItem, + String threeDItemWithWater, + Requirement[] placeRequirements, + Requirement[] breakRequirements, + Requirement[] useRequirements, + Action[] workActions, + Action[] interactActions, + Action[] reachLimitActions, + Action[] addWaterActions, + Action[] placeActions, + Action[] breakActions, + Action[] fullWaterActions, + WateringMethod[] wateringMethods + ) { + this.id = id; + this.existenceForm = existenceForm; + this.storage = storage; + this.range = range; + this.infinite = infinite; + this.sprinklingAmount = sprinklingAmount; + this.potWhitelist = potWhitelist; + this.waterBar = waterBar; + this.twoDItem = twoDItem; + this.threeDItem = Objects.requireNonNull(threeDItem); + this.threeDItemWithWater = Objects.requireNonNullElse(threeDItemWithWater, threeDItem); + this.placeRequirements = placeRequirements; + this.breakRequirements = breakRequirements; + this.useRequirements = useRequirements; + this.workActions = workActions; + this.interactActions = interactActions; + this.reachLimitActions = reachLimitActions; + this.addWaterActions = addWaterActions; + this.placeActions = placeActions; + this.breakActions = breakActions; + this.fullWaterActions = fullWaterActions; + this.modelIDs.add(twoDItem); + this.modelIDs.add(threeDItem); + this.modelIDs.add(threeDItemWithWater); + this.wateringMethods = wateringMethods; + } + + @Override + public String id() { + return id; + } + + @Override + public int storage() { + return storage; + } + + @Override + public int[][] range() { + return range; + } + + @Override + public boolean infinite() { + return infinite; + } + + @Override + public int sprinklingAmount() { + return sprinklingAmount; + } + + @Override + public String twoDItem() { + return twoDItem; + } + + @NotNull + @Override + public String threeDItem() { + return threeDItem; + } + + @NotNull + @Override + public String threeDItemWithWater() { + return threeDItemWithWater; + } + + @NotNull + @Override + public Set potWhitelist() { + return potWhitelist; + } + + @NotNull + @Override + public Set modelIDs() { + return modelIDs; + } + + @Override + public WaterBar waterBar() { + return waterBar; + } + + @NotNull + @Override + public ExistenceForm existenceForm() { + return existenceForm; + } + + @Override + public Requirement[] placeRequirements() { + return placeRequirements; + } + + @Override + public Requirement[] breakRequirements() { + return breakRequirements; + } + + @Override + public Requirement[] useRequirements() { + return useRequirements; + } + + @Override + public Action[] workActions() { + return workActions; + } + + @Override + public Action[] interactActions() { + return interactActions; + } + + @Override + public Action[] placeActions() { + return placeActions; + } + + @Override + public Action[] breakActions() { + return breakActions; + } + + @Override + public Action[] addWaterActions() { + return addWaterActions; + } + + @Override + public Action[] reachLimitActions() { + return reachLimitActions; + } + + @Override + public Action[] fullWaterActions() { + return fullWaterActions; + } + + @NotNull + @Override + public WateringMethod[] wateringMethods() { + return wateringMethods == null ? new WateringMethod[0] : wateringMethods; + } + + public static class BuilderImpl implements Builder { + private String id; + private ExistenceForm existenceForm; + private int storage; + private int[][] range; + private boolean infinite; + private int sprinklingAmount; + private Set potWhitelist; + private WaterBar waterBar; + private Requirement[] placeRequirements; + private Requirement[] breakRequirements; + private Requirement[] useRequirements; + private Action[] workActions; + private Action[] interactActions; + private Action[] reachLimitActions; + private Action[] addWaterActions; + private Action[] placeActions; + private Action[] breakActions; + private Action[] fullWaterActions; + private String twoDItem; + private String threeDItem; + private String threeDItemWithWater; + private WateringMethod[] wateringMethods; + + @Override + public SprinklerConfig build() { + return new SprinklerConfigImpl(id, existenceForm, storage, range, infinite, sprinklingAmount, potWhitelist, waterBar, twoDItem, threeDItem, threeDItemWithWater, + placeRequirements, breakRequirements, useRequirements, workActions, interactActions, reachLimitActions, addWaterActions, placeActions, breakActions, fullWaterActions, wateringMethods); + } + + @Override + public Builder id(String id) { + this.id = id; + return this; + } + + @Override + public Builder existenceForm(ExistenceForm existenceForm) { + this.existenceForm = existenceForm; + return this; + } + + @Override + public Builder storage(int storage) { + this.storage = storage; + return this; + } + + @Override + public Builder range(int[][] range) { + this.range = range; + return this; + } + + @Override + public Builder infinite(boolean infinite) { + this.infinite = infinite; + return this; + } + + @Override + public Builder sprinklingAmount(int sprinklingAmount) { + this.sprinklingAmount = sprinklingAmount; + return this; + } + + @Override + public Builder potWhitelist(Set potWhitelist) { + this.potWhitelist = new HashSet<>(potWhitelist); + return this; + } + + @Override + public Builder waterBar(WaterBar waterBar) { + this.waterBar = waterBar; + return this; + } + + @Override + public Builder twoDItem(String twoDItem) { + this.twoDItem = twoDItem; + return this; + } + + @Override + public Builder threeDItem(String threeDItem) { + this.threeDItem = threeDItem; + return this; + } + + @Override + public Builder threeDItemWithWater(String threeDItemWithWater) { + this.threeDItemWithWater = threeDItemWithWater; + return this; + } + + @Override + public Builder placeRequirements(Requirement[] placeRequirements) { + this.placeRequirements = placeRequirements; + return this; + } + + @Override + public Builder breakRequirements(Requirement[] breakRequirements) { + this.breakRequirements = breakRequirements; + return this; + } + + @Override + public Builder useRequirements(Requirement[] useRequirements) { + this.useRequirements = useRequirements; + return this; + } + + @Override + public Builder workActions(Action[] workActions) { + this.workActions = workActions; + return this; + } + + @Override + public Builder interactActions(Action[] interactActions) { + this.interactActions = interactActions; + return this; + } + + @Override + public Builder addWaterActions(Action[] addWaterActions) { + this.addWaterActions = addWaterActions; + return this; + } + + @Override + public Builder reachLimitActions(Action[] reachLimitActions) { + this.reachLimitActions = reachLimitActions; + return this; + } + + @Override + public Builder placeActions(Action[] placeActions) { + this.placeActions = placeActions; + return this; + } + + @Override + public Builder breakActions(Action[] breakActions) { + this.breakActions = breakActions; + return this; + } + + @Override + public Builder fullWaterActions(Action[] fullWaterActions) { + this.fullWaterActions = fullWaterActions; + return this; + } + + @Override + public Builder wateringMethods(WateringMethod[] wateringMethods) { + this.wateringMethods = wateringMethods; + return this; + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/VariationCrop.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java similarity index 67% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/VariationCrop.java rename to api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java index dc5d1b91b..dd8f30c43 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/VariationCrop.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java @@ -15,31 +15,31 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.item.impl; +package net.momirealms.customcrops.api.core.block; -import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; +import net.momirealms.customcrops.api.core.ExistenceForm; -public class VariationCrop { +public class VariationData { private final String id; - private final ItemCarrier itemMode; + private final ExistenceForm form; private final double chance; - public VariationCrop(String id, ItemCarrier itemMode, double chance) { + public VariationData(String id, ExistenceForm form, double chance) { this.id = id; - this.itemMode = itemMode; + this.form = form; this.chance = chance; } - public String getItemID() { + public String id() { return id; } - public ItemCarrier getItemCarrier() { - return itemMode; + public ExistenceForm existenceForm() { + return form; } - public double getChance() { + public double chance() { return chance; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java new file mode 100644 index 000000000..b6c307fa5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java @@ -0,0 +1,29 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.common.util.Key; +import net.momirealms.customcrops.api.core.InteractionResult; +import net.momirealms.customcrops.api.core.wrapper.WrappedInteractAirEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; + +public abstract class AbstractCustomCropsItem implements CustomCropsItem { + + private final Key type; + + public AbstractCustomCropsItem(Key type) { + this.type = type; + } + + @Override + public Key type() { + return type; + } + + @Override + public InteractionResult interactAt(WrappedInteractEvent wrapped) { + return InteractionResult.PASS; + } + + @Override + public void interactAir(WrappedInteractAirEvent event) { + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java new file mode 100644 index 000000000..f0f608862 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java @@ -0,0 +1,121 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.Objects; +import java.util.Set; + +public abstract class AbstractFertilizerConfig implements FertilizerConfig { + + protected String id; + protected String itemID; + protected String icon; + protected int times; + protected boolean beforePlant; + protected Set whitelistPots; + protected Requirement[] requirements; + protected Action[] beforePlantActions; + protected Action[] useActions; + protected Action[] wrongPotActions; + + public AbstractFertilizerConfig( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions + ) { + this.id = Objects.requireNonNull(id); + this.itemID = Objects.requireNonNull(itemID); + this.beforePlant = beforePlant; + this.times = times; + this.icon = icon; + this.requirements = requirements; + this.whitelistPots = whitelistPots; + this.beforePlantActions = beforePlantActions; + this.useActions = useActions; + this.wrongPotActions = wrongPotActions; + } + + @Override + public Action[] beforePlantActions() { + return beforePlantActions; + } + + @Override + public Action[] useActions() { + return useActions; + } + + @Override + public Action[] wrongPotActions() { + return wrongPotActions; + } + + @Override + public boolean beforePlant() { + return beforePlant; + } + + @Override + public Set whitelistPots() { + return whitelistPots; + } + + @Override + public String icon() { + return icon; + } + + @Override + public int times() { + return times; + } + + @Override + public String id() { + return id; + } + + @Override + public Requirement[] requirements() { + return requirements; + } + + @Override + public String itemID() { + return itemID; + } + + @Override + public int processGainPoints(int previousPoints) { + return previousPoints; + } + + @Override + public int processWaterToLose(int waterToLose) { + return waterToLose; + } + + @Override + public double processVariationChance(double previousChance) { + return previousChance; + } + + @Override + public int processDroppedItemAmount(int amount) { + return amount; + } + + @Override + public double[] overrideQualityRatio() { + return null; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java new file mode 100644 index 000000000..6358f3f31 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java @@ -0,0 +1,15 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.common.util.Key; +import net.momirealms.customcrops.api.core.InteractionResult; +import net.momirealms.customcrops.api.core.wrapper.WrappedInteractAirEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; + +public interface CustomCropsItem { + + Key type(); + + InteractionResult interactAt(WrappedInteractEvent event); + + void interactAir(WrappedInteractAirEvent event); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java new file mode 100644 index 000000000..b685e7ab5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java @@ -0,0 +1,40 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.core.Registries; +import org.jetbrains.annotations.Nullable; + +public interface Fertilizer { + + String id(); + + int times(); + + boolean reduceTimes(); + + // Flexibility matters more than performance + default FertilizerType type() { + FertilizerConfig config = Registries.FERTILIZER.get(id()); + if (config == null) { + return FertilizerType.INVALID; + } + return config.type(); + } + + @Nullable + default FertilizerConfig config() { + return Registries.FERTILIZER.get(id()); + } + + static Builder builder() { + return new FertilizerImpl.BuilderImpl(); + } + + interface Builder { + + Fertilizer build(); + + Builder id(String id); + + Builder times(int times); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java new file mode 100644 index 000000000..e556e5713 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java @@ -0,0 +1,44 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public interface FertilizerConfig { + + String id(); + + FertilizerType type(); + + boolean beforePlant(); + + String icon(); + + Requirement[] requirements(); + + String itemID(); + + int times(); + + Set whitelistPots(); + + Action[] beforePlantActions(); + + Action[] useActions(); + + Action[] wrongPotActions(); + + int processGainPoints(int previousPoints); + + int processWaterToLose(int waterToLose); + + double processVariationChance(double previousChance); + + int processDroppedItemAmount(int amount); + + @Nullable + double[] overrideQualityRatio(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java new file mode 100644 index 000000000..562d7f8f9 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java @@ -0,0 +1,51 @@ +package net.momirealms.customcrops.api.core.item; + +public class FertilizerImpl implements Fertilizer { + + private final String id; + private int times; + + public FertilizerImpl(String id, int times) { + this.id = id; + this.times = times; + } + + @Override + public String id() { + return id; + } + + @Override + public int times() { + return times; + } + + @Override + public boolean reduceTimes() { + times--; + return times <= 0; + } + + public static class BuilderImpl implements Fertilizer.Builder { + + private String id; + private int times; + + @Override + public Fertilizer build() { + return new FertilizerImpl(id, times); + } + + @Override + public Builder id(String id) { + this.id = id; + return this; + } + + @Override + public Builder times(int times) { + this.times = times; + return this; + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java new file mode 100644 index 000000000..47724e996 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java @@ -0,0 +1,113 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; +import net.momirealms.customcrops.api.core.BuiltInItemMechanics; +import net.momirealms.customcrops.api.core.InteractionResult; +import net.momirealms.customcrops.api.core.Registries; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.block.PotBlock; +import net.momirealms.customcrops.api.core.block.PotConfig; +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.WrappedInteractEvent; +import net.momirealms.customcrops.api.event.FertilizerUseEvent; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.api.util.EventUtils; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Optional; + +public class FertilizerItem extends AbstractCustomCropsItem { + + public FertilizerItem() { + super(BuiltInItemMechanics.FERTILIZER.key()); + } + + @Override + public InteractionResult interactAt(WrappedInteractEvent event) { + FertilizerConfig fertilizerConfig = Registries.FERTILIZER.get(event.itemID()); + if (fertilizerConfig == null) { + return InteractionResult.FAIL; + } + + final Player player = event.player(); + final Context context = Context.player(player); + final CustomCropsWorld world = event.world(); + final ItemStack itemInHand = event.itemInHand(); + String targetBlockID = event.relatedID(); + Location targetLocation = event.location(); + + // if the clicked block is a crop, correct the target block + List cropConfigs = Registries.STAGE_TO_CROP_UNSAFE.get(event.relatedID()); + if (cropConfigs != null) { + // is a crop + targetLocation = targetLocation.subtract(0,1,0); + targetBlockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(targetLocation); + } + + // if the clicked block is a pot + PotConfig potConfig = Registries.ITEM_TO_POT.get(targetBlockID); + if (potConfig != null) { + // check pot whitelist + if (!fertilizerConfig.whitelistPots().contains(potConfig.id())) { + ActionManager.trigger(context, fertilizerConfig.wrongPotActions()); + return InteractionResult.FAIL; + } + // check requirements + if (!RequirementManager.isSatisfied(context, fertilizerConfig.requirements())) { + return InteractionResult.FAIL; + } + if (!RequirementManager.isSatisfied(context, potConfig.useRequirements())) { + return InteractionResult.FAIL; + } + // check "before-plant" + if (fertilizerConfig.beforePlant()) { + Location cropLocation = targetLocation.clone().add(0,1,0); + Optional state = world.getBlockState(Pos3.from(cropLocation)); + if (state.isPresent()) { + CustomCropsBlockState blockState = state.get(); + if (blockState.type() instanceof CropBlock) { + ActionManager.trigger(context, fertilizerConfig.beforePlantActions()); + return InteractionResult.FAIL; + } + } + } + + PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); + assert potBlock != null; + // fix or get data + Fertilizer fertilizer = Fertilizer.builder() + .times(fertilizerConfig.times()) + .id(fertilizerConfig.id()) + .build(); + CustomCropsBlockState potState = potBlock.fixOrGetState(world, Pos3.from(targetLocation), potConfig, event.relatedID()); + if (!potBlock.canApplyFertilizer(potState,fertilizer)) { + return InteractionResult.FAIL; + } + // trigger event + FertilizerUseEvent useEvent = new FertilizerUseEvent(player, itemInHand, fertilizer, targetLocation, potState, event.hand(), potConfig); + if (EventUtils.fireAndCheckCancel(useEvent)) + return InteractionResult.FAIL; + // add the fertilizer + if (potBlock.addFertilizer(potState, fertilizer)) { + potBlock.updateBlockAppearance(targetLocation, potState, potBlock.fertilizers(potState)); + } + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemInHand.getAmount() - 1); + } + ActionManager.trigger(context, fertilizerConfig.useActions()); + return InteractionResult.SUCCESS; + } + + return InteractionResult.PASS; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java new file mode 100644 index 000000000..46426524e --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) <2022> + * + * 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.core.item; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.common.util.TriFunction; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.Objects; + +public class FertilizerType { + + public static final FertilizerType SPEED_GROW = of("speed_grow", + (manager, id, section) -> { + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + return SpeedGrow.create( + id, section.getString("item"), + section.getInt("times", 14), section.getString("icon", ""), + section.getBoolean("before-plant", false), + new HashSet<>(section.getStringList("pot-whitelist")), + BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class).parseRequirements(section.getSection("requirements"), true), + pam.parseActions(section.getSection("events.before_plant")), + pam.parseActions(section.getSection("events.use")), + pam.parseActions(section.getSection("events.wrong_pot")), + manager.getIntChancePair(section.getSection("chance")) + ); + } + ); + public static final FertilizerType QUALITY = of("quality", + (manager, id, section) -> { + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + return Quality.create( + id, section.getString("item"), + section.getInt("times", 14), section.getString("icon", ""), + section.getBoolean("before-plant", false), + new HashSet<>(section.getStringList("pot-whitelist")), + BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class).parseRequirements(section.getSection("requirements"), true), + pam.parseActions(section.getSection("events.before_plant")), + pam.parseActions(section.getSection("events.use")), + pam.parseActions(section.getSection("events.wrong_pot")), + section.getDouble("chance", 1d), + manager.getQualityRatio(section.getString("ratio")) + ); + } + ); + public static final FertilizerType SOIL_RETAIN = of("soil_retain", + (manager, id, section) -> { + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + return SoilRetain.create( + id, section.getString("item"), + section.getInt("times", 14), section.getString("icon", ""), + section.getBoolean("before-plant", false), + new HashSet<>(section.getStringList("pot-whitelist")), + BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class).parseRequirements(section.getSection("requirements"), true), + pam.parseActions(section.getSection("events.before_plant")), + pam.parseActions(section.getSection("events.use")), + pam.parseActions(section.getSection("events.wrong_pot")), + section.getDouble("chance", 1d) + ); + } + ); + public static final FertilizerType VARIATION = of("variation", + (manager, id, section) -> { + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + return Variation.create( + id, section.getString("item"), + section.getInt("times", 14), section.getString("icon", ""), + section.getBoolean("before-plant", false), + new HashSet<>(section.getStringList("pot-whitelist")), + BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class).parseRequirements(section.getSection("requirements"), true), + pam.parseActions(section.getSection("events.before_plant")), + pam.parseActions(section.getSection("events.use")), + pam.parseActions(section.getSection("events.wrong_pot")), + section.getBoolean("addOrMultiply", true), + section.getDouble("chance", 0.01d) + ); + } + ); + public static final FertilizerType YIELD_INCREASE = of("yield_increase", + (manager, id, section) -> { + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + return YieldIncrease.create( + id, section.getString("item"), + section.getInt("times", 14), section.getString("icon", ""), + section.getBoolean("before-plant", false), + new HashSet<>(section.getStringList("pot-whitelist")), + BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class).parseRequirements(section.getSection("requirements"), true), + pam.parseActions(section.getSection("events.before_plant")), + pam.parseActions(section.getSection("events.use")), + pam.parseActions(section.getSection("events.wrong_pot")), + manager.getIntChancePair(section.getSection("chance")) + ); + } + ); + public static final FertilizerType INVALID = of("invalid", + (manager, id, section) -> null + ); + + private final String id; + private final TriFunction argumentConsumer; + + public FertilizerType(String id, TriFunction argumentConsumer) { + this.id = id; + this.argumentConsumer = argumentConsumer; + } + + public String id() { + return id; + } + + public static FertilizerType of(String id, TriFunction argumentConsumer) { + return new FertilizerType(id, argumentConsumer); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FertilizerType that = (FertilizerType) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + public FertilizerConfig parse(ConfigManager manager, String id, Section section) { + return argumentConsumer.apply(manager, id, section); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java new file mode 100644 index 000000000..e7ab798c0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java @@ -0,0 +1,31 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.Set; + +public interface Quality extends FertilizerConfig { + + double chance(); + + double[] ratio(); + + static Quality create( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + double chance, + double[] ratio + ) { + return new QualityImpl(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions, chance, ratio); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java new file mode 100644 index 000000000..6370c93f0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java @@ -0,0 +1,52 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.Set; + +public class QualityImpl extends AbstractFertilizerConfig implements Quality { + + private final double chance; + private final double[] ratio; + + protected QualityImpl( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + double chance, + double[] ratio + ) { + super(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions); + this.chance = chance; + this.ratio = ratio; + } + + @Override + public double chance() { + return chance; + } + + @Override + public double[] ratio() { + return ratio; + } + + @Override + public FertilizerType type() { + return FertilizerType.QUALITY; + } + + @Override + public double[] overrideQualityRatio() { + return ratio(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java new file mode 100644 index 000000000..ea7fc87d0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java @@ -0,0 +1,130 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.block.CropStageConfig; +import net.momirealms.customcrops.api.core.block.PotConfig; +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.WrappedInteractEvent; +import net.momirealms.customcrops.api.event.CropPlantEvent; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Collection; +import java.util.Map; + +public class SeedItem extends AbstractCustomCropsItem { + + public SeedItem() { + super(BuiltInBlockMechanics.CROP.key()); + } + + @Override + public InteractionResult interactAt(WrappedInteractEvent event) { + // check if it's a pot + PotConfig potConfig = Registries.ITEM_TO_POT.get(event.relatedID()); + if (potConfig == null) return InteractionResult.PASS; + // check if the crop exists + CropConfig cropConfig = Registries.SEED_TO_CROP.get(event.itemID()); + if (cropConfig == null) return InteractionResult.FAIL; + // check the block face + if (event.clickedBlockFace() != BlockFace.UP) + return InteractionResult.PASS; + + final Player player = event.player(); + Context context = Context.player(player); + // check pot whitelist + if (!cropConfig.potWhitelist().contains(potConfig.id())) { + ActionManager.trigger(context, cropConfig.wrongPotActions()); + } + // check plant requirements + if (!RequirementManager.isSatisfied(context, cropConfig.plantRequirements())) { + return InteractionResult.FAIL; + } + // check if the block is empty + if (!suitableForSeed(event.location())) { + return InteractionResult.FAIL; + } + CustomCropsWorld world = event.world(); + Location seedLocation = event.location().add(0, 1, 0); + Pos3 pos3 = Pos3.from(seedLocation); + // check limitation + if (world.setting().cropPerChunk() >= 0) { + if (world.testChunkLimitation(pos3, CropBlock.class, world.setting().cropPerChunk())) { + ActionManager.trigger(context, cropConfig.reachLimitActions()); + return InteractionResult.FAIL; + } + } + final ItemStack itemInHand = event.itemInHand(); + + CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState(); + CropBlock cropBlock = (CropBlock) state.type(); + cropBlock.id(state, cropConfig.id()); + // trigger event + CropPlantEvent plantEvent = new CropPlantEvent(player, itemInHand, event.hand(), seedLocation, cropConfig, state, 0); + if (EventUtils.fireAndCheckCancel(plantEvent)) { + return InteractionResult.FAIL; + } + int point = plantEvent.getPoint(); + int temp = point; + ExistenceForm form = null; + String stageID = null; + while (temp >= 0) { + Map.Entry entry = cropConfig.getFloorStageEntry(temp); + CropStageConfig stageConfig = entry.getValue(); + if (stageConfig.stageID() != null) { + form = stageConfig.existenceForm(); + stageID = stageConfig.stageID(); + break; + } + temp = stageConfig.point() - 1; + } + if (stageID == null || form == null) { + return InteractionResult.FAIL; + } + // reduce item + if (player.getGameMode() != GameMode.CREATIVE) + itemInHand.setAmount(itemInHand.getAmount() - 1); + // place model + BukkitCustomCropsPlugin.getInstance().getItemManager().place(seedLocation, form, stageID, cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE); + cropBlock.point(state, point); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + + // set slot arg + context.arg(ContextKeys.SLOT, event.hand()); + // trigger plant actions + ActionManager.trigger(context, cropConfig.plantActions()); + return InteractionResult.SUCCESS; + } + + private boolean suitableForSeed(Location location) { + Block block = location.getBlock(); + if (block.getType() != Material.AIR) return false; + Location center = LocationUtils.toBlockCenterLocation(location); + Collection entities = center.getWorld().getNearbyEntities(center, 0.5,0.51,0.5); + entities.removeIf(entity -> (entity instanceof Player || entity instanceof Item)); + return entities.isEmpty(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java new file mode 100644 index 000000000..70f39e51b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java @@ -0,0 +1,28 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.Set; + +public interface SoilRetain extends FertilizerConfig { + + double chance(); + + static SoilRetain create( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + double chance + ) { + return new SoilRetainImpl(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions, chance); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java new file mode 100644 index 000000000..965d58c79 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java @@ -0,0 +1,44 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.Set; + +public class SoilRetainImpl extends AbstractFertilizerConfig implements SoilRetain { + + private final double chance; + + public SoilRetainImpl( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + double chance + ) { + super(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions); + this.chance = chance; + } + + @Override + public double chance() { + return chance; + } + + @Override + public FertilizerType type() { + return FertilizerType.SOIL_RETAIN; + } + + @Override + public int processWaterToLose(int waterToLose) { + return Math.min(waterToLose, 0); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java new file mode 100644 index 000000000..141cb6b2b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java @@ -0,0 +1,30 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Set; + +public interface SpeedGrow extends FertilizerConfig { + + int pointBonus(); + + static SpeedGrow create( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + List> chances + ) { + return new SpeedGrowImpl(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions, chances); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java new file mode 100644 index 000000000..0734d2f4e --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java @@ -0,0 +1,51 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Set; + +public class SpeedGrowImpl extends AbstractFertilizerConfig implements SpeedGrow { + + private final List> chances; + + public SpeedGrowImpl( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + List> chances + ) { + super(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions); + this.chances = chances; + } + + @Override + public int pointBonus() { + for (Pair pair : chances) { + if (Math.random() < pair.left()) { + return pair.right(); + } + } + return 0; + } + + @Override + public FertilizerType type() { + return FertilizerType.SPEED_GROW; + } + + @Override + public int processGainPoints(int previousPoints) { + return pointBonus() + previousPoints; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java new file mode 100644 index 000000000..eebd8dec4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java @@ -0,0 +1,111 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.block.SprinklerBlock; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +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.WrappedInteractEvent; +import net.momirealms.customcrops.api.event.SprinklerPlaceEvent; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Collection; + +public class SprinklerItem extends AbstractCustomCropsItem { + + public SprinklerItem() { + super(BuiltInItemMechanics.SPRINKLER_ITEM.key()); + } + + @Override + public InteractionResult interactAt(WrappedInteractEvent event) { + // should be place on block + if (event.existenceForm() != ExistenceForm.BLOCK) + return InteractionResult.PASS; + SprinklerConfig config = Registries.SPRINKLER.get(event.itemID()); + if (config == null) { + return InteractionResult.FAIL; + } + + Block clicked = event.location().getBlock(); + Location targetLocation; + if (clicked.isReplaceable()) { + targetLocation = event.location(); + } else { + if (event.clickedBlockFace() != BlockFace.UP) + return InteractionResult.PASS; + if (!clicked.isSolid()) + return InteractionResult.PASS; + targetLocation = event.location().clone().add(0,1,0); + if (!suitableForSprinkler(targetLocation)) { + return InteractionResult.PASS; + } + } + + final Player player = event.player(); + final ItemStack itemInHand = event.itemInHand(); + Context context = Context.player(player); + // check requirements + if (!RequirementManager.isSatisfied(context, config.placeRequirements())) { + return InteractionResult.FAIL; + } + + final CustomCropsWorld world = event.world(); + Pos3 pos3 = Pos3.from(targetLocation); + // check limitation + if (world.setting().sprinklerPerChunk() >= 0) { + if (world.testChunkLimitation(pos3, SprinklerBlock.class, world.setting().sprinklerPerChunk())) { + ActionManager.trigger(context, config.reachLimitActions()); + return InteractionResult.FAIL; + } + } + // generate state + CustomCropsBlockState state = BuiltInBlockMechanics.SPRINKLER.createBlockState(); + SprinklerBlock sprinklerBlock = (SprinklerBlock) BuiltInBlockMechanics.SPRINKLER.mechanic(); + sprinklerBlock.id(state, config.id()); + sprinklerBlock.water(state, 0); + // trigger event + SprinklerPlaceEvent placeEvent = new SprinklerPlaceEvent(player, itemInHand, event.hand(), targetLocation.clone(), config, state); + if (EventUtils.fireAndCheckCancel(placeEvent)) + return InteractionResult.FAIL; + // clear replaceable block + targetLocation.getBlock().setType(Material.AIR, false); + if (player.getGameMode() != GameMode.CREATIVE) + itemInHand.setAmount(itemInHand.getAmount() - 1); + + // place the sprinkler + BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(targetLocation), config.existenceForm(), config.threeDItem(), FurnitureRotation.NONE); + world.addBlockState(pos3, state).ifPresent(previous -> { + BukkitCustomCropsPlugin.getInstance().debug( + "Overwrite old data with " + state.compoundMap().toString() + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + ); + }); + ActionManager.trigger(context, config.placeActions()); + return InteractionResult.SUCCESS; + } + + private boolean suitableForSprinkler(Location location) { + Block block = location.getBlock(); + if (block.getType() != Material.AIR) return false; + Location center = LocationUtils.toBlockCenterLocation(location); + Collection entities = center.getWorld().getNearbyEntities(center, 0.5,0.51,0.5); + entities.removeIf(entity -> (entity instanceof Player || entity instanceof Item)); + return entities.isEmpty(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java new file mode 100644 index 000000000..289b86b18 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java @@ -0,0 +1,31 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.Set; + +public interface Variation extends FertilizerConfig { + + double chanceBonus(); + + boolean addOrMultiply(); + + static Variation create( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + boolean addOrMultiply, + double chance + ) { + return new VariationImpl(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions, addOrMultiply, chance); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java new file mode 100644 index 000000000..45b438408 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java @@ -0,0 +1,56 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.Set; + +public class VariationImpl extends AbstractFertilizerConfig implements Variation { + + private final double chance; + private final boolean addOrMultiply; + + public VariationImpl( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + boolean addOrMultiply, + double chance + ) { + super(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions); + this.chance = chance; + this.addOrMultiply = addOrMultiply; + } + + @Override + public double chanceBonus() { + return chance; + } + + @Override + public boolean addOrMultiply() { + return addOrMultiply; + } + + @Override + public FertilizerType type() { + return FertilizerType.VARIATION; + } + + @Override + public double processVariationChance(double previousChance) { + if (addOrMultiply()) { + return previousChance + chanceBonus(); + } else { + return previousChance * chanceBonus(); + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java new file mode 100644 index 000000000..68ddbef36 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java @@ -0,0 +1,108 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.water.FillMethod; +import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface WateringCanConfig { + + String id(); + + String itemID(); + + int width(); + + int length(); + + int storage(); + + int wateringAmount(); + + boolean dynamicLore(); + + Set whitelistPots(); + + Set whitelistSprinklers(); + + List> lore(); + + WaterBar waterBar(); + + Requirement[] requirements(); + + boolean infinite(); + + Integer appearance(int water); + + Action[] fullActions(); + + Action[] addWaterActions(); + + Action[] consumeWaterActions(); + + Action[] runOutOfWaterActions(); + + Action[] wrongPotActions(); + + Action[] wrongSprinklerActions(); + + FillMethod[] fillMethods(); + + static Builder builder() { + return new WateringCanConfigImpl.BuilderImpl(); + } + + interface Builder { + + WateringCanConfig build(); + + Builder id(String id); + + Builder itemID(String itemID); + + Builder width(int width); + + Builder length(int length); + + Builder storage(int storage); + + Builder wateringAmount(int wateringAmount); + + Builder dynamicLore(boolean dynamicLore); + + Builder potWhitelist(Set whitelistPots); + + Builder sprinklerWhitelist(Set whitelistSprinklers); + + Builder lore(List> lore); + + Builder waterBar(WaterBar waterBar); + + Builder requirements(Requirement[] requirements); + + Builder infinite(boolean infinite); + + Builder appearances(Map appearances); + + Builder fullActions(Action[] fullActions); + + Builder addWaterActions(Action[] addWaterActions); + + Builder consumeWaterActions(Action[] consumeWaterActions); + + Builder runOutOfWaterActions(Action[] runOutOfWaterActions); + + Builder wrongPotActions(Action[] wrongPotActions); + + Builder wrongSprinklerActions(Action[] wrongSprinklerActions); + + Builder fillMethods(FillMethod[] fillMethods); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java new file mode 100644 index 000000000..1906816be --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java @@ -0,0 +1,342 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.core.water.FillMethod; +import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.*; + +public class WateringCanConfigImpl implements WateringCanConfig { + + private final String id; + private final String itemID; + private final int width; + private final int length; + private final int storage; + private final int wateringAmount; + private final boolean dynamicLore; + private final Set whitelistPots; + private final Set whitelistSprinklers; + private final List> lore; + private final WaterBar waterBar; + private final Requirement[] requirements; + private final boolean infinite; + private final HashMap appearances; + private final Action[] fullActions; + private final Action[] addWaterActions; + private final Action[] consumeWaterActions; + private final Action[] runOutOfWaterActions; + private final Action[] wrongPotActions; + private final Action[] wrongSprinklerActions; + private final FillMethod[] fillMethods; + + public WateringCanConfigImpl( + String id, + String itemID, + int width, + int length, + int storage, + int wateringAmount, + boolean dynamicLore, + Set whitelistPots, + Set whitelistSprinklers, + List> lore, + WaterBar waterBar, + Requirement[] requirements, + boolean infinite, + HashMap appearances, + Action[] fullActions, + Action[] addWaterActions, + Action[] consumeWaterActions, + Action[] runOutOfWaterActions, + Action[] wrongPotActions, + Action[] wrongSprinklerActions, + FillMethod[] fillMethods + ) { + this.id = id; + this.itemID = itemID; + this.width = width; + this.length = length; + this.storage = storage; + this.wateringAmount = wateringAmount; + this.dynamicLore = dynamicLore; + this.whitelistPots = whitelistPots; + this.whitelistSprinklers = whitelistSprinklers; + this.lore = lore; + this.waterBar = waterBar; + this.requirements = requirements; + this.infinite = infinite; + this.appearances = appearances; + this.fullActions = fullActions; + this.addWaterActions = addWaterActions; + this.consumeWaterActions = consumeWaterActions; + this.runOutOfWaterActions = runOutOfWaterActions; + this.wrongPotActions = wrongPotActions; + this.wrongSprinklerActions = wrongSprinklerActions; + this.fillMethods = fillMethods; + } + + @Override + public String id() { + return id; + } + + @Override + public String itemID() { + return itemID; + } + + @Override + public int width() { + return width; + } + + @Override + public int length() { + return length; + } + + @Override + public int storage() { + return storage; + } + + @Override + public int wateringAmount() { + return wateringAmount; + } + + @Override + public boolean dynamicLore() { + return dynamicLore; + } + + @Override + public Set whitelistPots() { + return whitelistPots; + } + + @Override + public Set whitelistSprinklers() { + return whitelistSprinklers; + } + + @Override + public List> lore() { + return lore; + } + + @Override + public WaterBar waterBar() { + return waterBar; + } + + @Override + public Requirement[] requirements() { + return requirements; + } + + @Override + public boolean infinite() { + return infinite; + } + + @Override + public Integer appearance(int water) { + return appearances.get(water); + } + + @Override + public Action[] fullActions() { + return fullActions; + } + + @Override + public Action[] addWaterActions() { + return addWaterActions; + } + + @Override + public Action[] consumeWaterActions() { + return consumeWaterActions; + } + + @Override + public Action[] runOutOfWaterActions() { + return runOutOfWaterActions; + } + + @Override + public Action[] wrongPotActions() { + return wrongPotActions; + } + + @Override + public Action[] wrongSprinklerActions() { + return wrongSprinklerActions; + } + + @Override + public FillMethod[] fillMethods() { + return fillMethods; + } + + static class BuilderImpl implements Builder { + + private String id; + private String itemID; + private int width; + private int length; + private int storage; + private int wateringAmount; + private boolean dynamicLore; + private Set whitelistPots; + private Set whitelistSprinklers; + private List> lore; + private WaterBar waterBar; + private Requirement[] requirements; + private boolean infinite; + private HashMap appearances; + private Action[] fullActions; + private Action[] addWaterActions; + private Action[] consumeWaterActions; + private Action[] runOutOfWaterActions; + private Action[] wrongPotActions; + private Action[] wrongSprinklerActions; + private FillMethod[] fillMethods; + + @Override + public WateringCanConfig build() { + return new WateringCanConfigImpl(id, itemID, width, length, storage, wateringAmount, dynamicLore, whitelistPots, whitelistSprinklers, lore, waterBar, requirements, infinite, appearances, fullActions, addWaterActions, consumeWaterActions, runOutOfWaterActions, wrongPotActions, wrongSprinklerActions, fillMethods); + } + + @Override + public Builder id(String id) { + this.id = id; + return this; + } + + @Override + public Builder itemID(String itemID) { + this.itemID = itemID; + return this; + } + + @Override + public Builder width(int width) { + this.width = width; + return this; + } + + @Override + public Builder length(int length) { + this.length = length; + return this; + } + + @Override + public Builder storage(int storage) { + this.storage = storage; + return this; + } + + @Override + public Builder wateringAmount(int wateringAmount) { + this.wateringAmount = wateringAmount; + return this; + } + + @Override + public Builder dynamicLore(boolean dynamicLore) { + this.dynamicLore = dynamicLore; + return this; + } + + @Override + public Builder potWhitelist(Set whitelistPots) { + this.whitelistPots = new HashSet<>(whitelistPots); + return this; + } + + @Override + public Builder sprinklerWhitelist(Set whitelistSprinklers) { + this.whitelistSprinklers = new HashSet<>(whitelistSprinklers); + return this; + } + + @Override + public Builder lore(List> lore) { + this.lore = new ArrayList<>(lore); + return this; + } + + @Override + public Builder waterBar(WaterBar waterBar) { + this.waterBar = waterBar; + return this; + } + + @Override + public Builder requirements(Requirement[] requirements) { + this.requirements = requirements; + return this; + } + + @Override + public Builder infinite(boolean infinite) { + this.infinite = infinite; + return this; + } + + @Override + public Builder appearances(Map appearances) { + this.appearances = new HashMap<>(appearances); + return this; + } + + @Override + public Builder fullActions(Action[] fullActions) { + this.fullActions = fullActions; + return this; + } + + @Override + public Builder addWaterActions(Action[] addWaterActions) { + this.addWaterActions = addWaterActions; + return this; + } + + @Override + public Builder consumeWaterActions(Action[] consumeWaterActions) { + this.consumeWaterActions = consumeWaterActions; + return this; + } + + @Override + public Builder runOutOfWaterActions(Action[] runOutOfWaterActions) { + this.runOutOfWaterActions = runOutOfWaterActions; + return this; + } + + @Override + public Builder wrongPotActions(Action[] wrongPotActions) { + this.wrongPotActions = wrongPotActions; + return this; + } + + @Override + public Builder wrongSprinklerActions(Action[] wrongSprinklerActions) { + this.wrongSprinklerActions = wrongSprinklerActions; + return this; + } + + @Override + public Builder fillMethods(FillMethod[] fillMethods) { + this.fillMethods = fillMethods; + return this; + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java new file mode 100644 index 000000000..96bb2ef57 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java @@ -0,0 +1,358 @@ +package net.momirealms.customcrops.api.core.item; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ScoreComponent; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.block.*; +import net.momirealms.customcrops.api.core.water.FillMethod; +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.WrappedInteractAirEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.event.WateringCanFillEvent; +import net.momirealms.customcrops.api.event.WateringCanWaterPotEvent; +import net.momirealms.customcrops.api.event.WateringCanWaterSprinklerEvent; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.item.Item; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.FluidCollisionMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class WateringCanItem extends AbstractCustomCropsItem { + + public WateringCanItem() { + super(BuiltInItemMechanics.WATERING_CAN.key()); + } + + public int getCurrentWater(ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR) return 0; + Item wrapped = BukkitCustomCropsPlugin.getInstance().getItemManager().wrap(itemStack); + return (int) wrapped.getTag("CustomCrops", "water").orElse(0); + } + + public void setCurrentWater(ItemStack itemStack, WateringCanConfig config, int water, Context context) { + if (itemStack == null || itemStack.getType() == Material.AIR) return; + Item wrapped = BukkitCustomCropsPlugin.getInstance().getItemManager().wrap(itemStack); + int realWater = Math.min(config.storage(), water); + if (!config.infinite()) { + wrapped.setTag("CustomCrops", "water", realWater); + wrapped.maxDamage().ifPresent(max -> { + if (max <= 0) return; + int damage = (int) (max * (((double) config.storage() - realWater) / config.storage())); + wrapped.damage(damage); + }); + // set appearance + Optional.ofNullable(config.appearance(realWater)).ifPresent(wrapped::customModelData); + } + if (config.dynamicLore()) { + List lore = new ArrayList<>(wrapped.lore().orElse(List.of())); + if (ConfigManager.protectOriginalLore()) { + lore.removeIf(line -> { + Component component = AdventureHelper.jsonToComponent(line); + return component instanceof ScoreComponent scoreComponent + && scoreComponent.objective().equals("water") + && scoreComponent.name().equals("cc"); + }); + } else { + lore.clear(); + } + for (TextValue newLore : config.lore()) { + ScoreComponent.Builder builder = Component.score().name("cc").objective("water"); + builder.append(AdventureHelper.miniMessage(newLore.render(context))); + lore.add(AdventureHelper.componentToJson(builder.build())); + } + wrapped.lore(lore); + } + wrapped.load(); + } + + @Override + public void interactAir(WrappedInteractAirEvent event) { + WateringCanConfig config = Registries.WATERING_CAN.get(event.itemID()); + if (config == null) + return; + + final Player player = event.player();; + Context context = Context.player(player); + // check requirements + if (!RequirementManager.isSatisfied(context, config.requirements())) { + return; + } + // ignore infinite + if (config.infinite()) + return; + // get target block + Block targetBlock = player.getTargetBlockExact(5, FluidCollisionMode.ALWAYS); + if (targetBlock == null) + return; + + final ItemStack itemInHand = event.itemInHand(); + int water = getCurrentWater(itemInHand); + + String blockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(targetBlock); + if (targetBlock.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { + blockID = "WATER"; + } + + for (FillMethod method : config.fillMethods()) { + if (method.getID().equals(blockID)) { + if (method.checkRequirements(context)) { + if (water >= config.storage()) { + ActionManager.trigger(context, config.fullActions()); + return; + } + WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, event.hand(), itemInHand, targetBlock.getLocation(), config, method); + if (EventUtils.fireAndCheckCancel(fillEvent)) + return; + setCurrentWater(itemInHand, config, water + method.amountOfWater(), context); + method.triggerActions(context); + ActionManager.trigger(context, config.addWaterActions()); + } + return; + } + } + } + + @Override + public InteractionResult interactAt(WrappedInteractEvent event) { + WateringCanConfig wateringCanConfig = Registries.WATERING_CAN.get(event.itemID()); + if (wateringCanConfig == null) + return InteractionResult.FAIL; + + final Player player = event.player(); + final Context context = Context.player(player); + + // check watering can requirements + if (!RequirementManager.isSatisfied(context, wateringCanConfig.requirements())) { + return InteractionResult.FAIL; + } + + final CustomCropsWorld world = event.world(); + final ItemStack itemInHand = event.itemInHand(); + String targetBlockID = event.relatedID(); + Location targetLocation = event.location(); + BlockFace blockFace = event.clickedBlockFace(); + + int waterInCan = getCurrentWater(itemInHand); + + SprinklerConfig sprinklerConfig = Registries.SPRINKLER.get(targetBlockID); + if (sprinklerConfig != null) { + // ignore infinite sprinkler + if (sprinklerConfig.infinite()) { + return InteractionResult.FAIL; + } + // check requirements + if (!RequirementManager.isSatisfied(context, sprinklerConfig.useRequirements())) { + return InteractionResult.FAIL; + } + // check water + if (waterInCan <= 0 && !wateringCanConfig.infinite()) { + ActionManager.trigger(context, wateringCanConfig.runOutOfWaterActions()); + return InteractionResult.FAIL; + } + // check whitelist + if (!wateringCanConfig.whitelistSprinklers().contains(sprinklerConfig.id())) { + ActionManager.trigger(context, wateringCanConfig.wrongSprinklerActions()); + return InteractionResult.FAIL; + } + + SprinklerBlock sprinklerBlock = (SprinklerBlock) BuiltInBlockMechanics.SPRINKLER.mechanic(); + CustomCropsBlockState sprinklerState = sprinklerBlock.fixOrGetState(world, Pos3.from(targetLocation), sprinklerConfig, targetBlockID); + + // check full + if (sprinklerBlock.water(sprinklerState) >= sprinklerConfig.storage()) { + ActionManager.trigger(context, sprinklerConfig.fullWaterActions()); + return InteractionResult.FAIL; + } + + // trigger event + WateringCanWaterSprinklerEvent waterSprinklerEvent = new WateringCanWaterSprinklerEvent(player, itemInHand, event.hand(), wateringCanConfig, sprinklerConfig, sprinklerState, targetLocation); + if (EventUtils.fireAndCheckCancel(waterSprinklerEvent)) { + return InteractionResult.FAIL; + } + // add water + if (sprinklerBlock.addWater(sprinklerState, sprinklerConfig, wateringCanConfig.wateringAmount())) { + if (!sprinklerConfig.threeDItem().equals(sprinklerConfig.threeDItemWithWater())) { + sprinklerBlock.updateBlockAppearance(targetLocation, sprinklerConfig, true); + } + } + + ActionManager.trigger(context, wateringCanConfig.consumeWaterActions()); + setCurrentWater(itemInHand, wateringCanConfig, waterInCan - 1, context); + return InteractionResult.SUCCESS; + } + + // try filling the watering can + for (FillMethod method : wateringCanConfig.fillMethods()) { + if (method.getID().equals(event.relatedID())) { + if (method.checkRequirements(context)) { + if (waterInCan >= wateringCanConfig.storage()) { + ActionManager.trigger(context, wateringCanConfig.fullActions()); + return InteractionResult.FAIL; + } + WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, event.hand(), itemInHand, targetLocation, wateringCanConfig, method); + if (EventUtils.fireAndCheckCancel(fillEvent)) + return InteractionResult.FAIL; + setCurrentWater(itemInHand, wateringCanConfig, waterInCan + method.amountOfWater(), context); + method.triggerActions(context); + ActionManager.trigger(context, wateringCanConfig.addWaterActions()); + } + return InteractionResult.SUCCESS; + } + } + + // if the clicked block is a crop, correct the target block + List cropConfigs = Registries.STAGE_TO_CROP_UNSAFE.get(event.relatedID()); + if (cropConfigs != null) { + // is a crop + targetLocation = targetLocation.subtract(0,1,0); + targetBlockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(targetLocation); + blockFace = BlockFace.UP; + } + + PotConfig potConfig = Registries.ITEM_TO_POT.get(targetBlockID); + if (potConfig != null) { + // need to click the upper face + if (blockFace != BlockFace.UP) + return InteractionResult.PASS; + // check whitelist + if (!wateringCanConfig.whitelistPots().contains(potConfig.id())) { + ActionManager.trigger(context, wateringCanConfig.wrongPotActions()); + return InteractionResult.FAIL; + } + // check water + if (waterInCan <= 0 && !wateringCanConfig.infinite()) { + ActionManager.trigger(context, wateringCanConfig.runOutOfWaterActions()); + return InteractionResult.FAIL; + } + + World bukkitWorld = targetLocation.getWorld(); + ArrayList> pots = potInRange(bukkitWorld, Pos3.from(targetLocation), wateringCanConfig.width(), wateringCanConfig.length(), player.getLocation().getYaw(), potConfig); + + WateringCanWaterPotEvent waterPotEvent = new WateringCanWaterPotEvent(player, itemInHand, event.hand(), wateringCanConfig, potConfig, pots); + if (EventUtils.fireAndCheckCancel(waterPotEvent)) { + return InteractionResult.FAIL; + } + + PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); + for (Pair pair : waterPotEvent.getPotWithIDs()) { + CustomCropsBlockState potState = potBlock.fixOrGetState(world,pair.left(), potConfig, pair.right()); + if (potBlock.addWater(potState, potConfig, wateringCanConfig.wateringAmount())) { + Location temp = pair.left().toLocation(bukkitWorld); + potBlock.updateBlockAppearance(temp, potConfig, true, potBlock.fertilizers(potState)); + context.arg(ContextKeys.LOCATION, temp); + ActionManager.trigger(context, potConfig.addWaterActions()); + } + } + + ActionManager.trigger(context, wateringCanConfig.consumeWaterActions()); + setCurrentWater(itemInHand, wateringCanConfig, waterInCan - 1, context); + return InteractionResult.SUCCESS; + } + + return InteractionResult.PASS; + } + + public ArrayList> potInRange(World world, Pos3 pos3, int width, int length, float yaw, PotConfig config) { + ArrayList potPos = new ArrayList<>(); + int extend = (width-1) / 2; + int extra = (width-1) % 2; + switch ((int) ((yaw + 180) / 45)) { + case 0 -> { + // -180 ~ -135 + for (int i = -extend; i <= extend + extra; i++) { + for (int j = 0; j < length; j++) { + potPos.add(pos3.add(i, 0, -j)); + } + } + } + case 1 -> { + // -135 ~ -90 + for (int i = -extend - extra; i <= extend; i++) { + for (int j = 0; j < length; j++) { + potPos.add(pos3.add(j, 0, i)); + } + } + } + case 2 -> { + // -90 ~ -45 + for (int i = -extend; i <= extend + extra; i++) { + for (int j = 0; j < length; j++) { + potPos.add(pos3.add(j, 0, i)); + } + } + } + case 3 -> { + // -45 ~ 0 + for (int i = -extend; i <= extend + extra; i++) { + for (int j = 0; j < length; j++) { + potPos.add(pos3.add(i, 0, j)); + } + } + } + case 4 -> { + // 0 ~ 45 + for (int i = -extend - extra; i <= extend; i++) { + for (int j = 0; j < length; j++) { + potPos.add(pos3.add(i, 0, j)); + } + } + } + case 5 -> { + // 45 ~ 90 + for (int i = -extend; i <= extend + extra; i++) { + for (int j = 0; j < length; j++) { + potPos.add(pos3.add(-j, 0, i)); + } + } + } + case 6 -> { + // 90 ~ 135 + for (int i = -extend - extra; i <= extend; i++) { + for (int j = 0; j < length; j++) { + potPos.add(pos3.add(-j, 0, i)); + } + } + } + case 7 -> { + // 135 ~ 180 + for (int i = -extend - extra; i <= extend; i++) { + for (int j = 0; j < length; j++) { + potPos.add(pos3.add(i, 0, -j)); + } + } + } + default -> potPos.add(pos3); + } + ItemManager itemManager = BukkitCustomCropsPlugin.getInstance().getItemManager(); + ArrayList> pots = new ArrayList<>(); + for (Pos3 loc : potPos) { + Block block = world.getBlockAt(loc.x(), loc.y(), loc.z()); + String blockID = itemManager.blockID(block); + PotConfig potConfig = Registries.ITEM_TO_POT.get(blockID); + if (potConfig == config) { + pots.add(Pair.of(loc, blockID)); + } + } + return pots; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java new file mode 100644 index 000000000..ed352b5ee --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java @@ -0,0 +1,30 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Set; + +public interface YieldIncrease extends FertilizerConfig { + + int amountBonus(); + + static YieldIncrease create( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + List> chances + ) { + return new YieldIncreaseImpl(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions, chances); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java new file mode 100644 index 000000000..d7da16b4d --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java @@ -0,0 +1,51 @@ +package net.momirealms.customcrops.api.core.item; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Set; + +public class YieldIncreaseImpl extends AbstractFertilizerConfig implements YieldIncrease { + + private final List> chances; + + public YieldIncreaseImpl( + String id, + String itemID, + int times, + String icon, + boolean beforePlant, + Set whitelistPots, + Requirement[] requirements, + Action[] beforePlantActions, + Action[] useActions, + Action[] wrongPotActions, + List> chances + ) { + super(id, itemID, times, icon, beforePlant, whitelistPots, requirements, beforePlantActions, useActions, wrongPotActions); + this.chances = chances; + } + + @Override + public int amountBonus() { + for (Pair pair : chances) { + if (Math.random() < pair.left()) { + return pair.right(); + } + } + return 0; + } + + @Override + public FertilizerType type() { + return FertilizerType.YIELD_INCREASE; + } + + @Override + public int processDroppedItemAmount(int amount) { + return amount + amountBonus(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/water/AbstractMethod.java b/api/src/main/java/net/momirealms/customcrops/api/core/water/AbstractMethod.java new file mode 100644 index 000000000..e634ffb90 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/water/AbstractMethod.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) <2022> + * + * 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.core.water; + +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import org.bukkit.entity.Player; + +public abstract class AbstractMethod { + + protected int amount; + private final Action[] actions; + private final Requirement[] requirements; + + protected AbstractMethod(int amount, Action[] actions, Requirement[] requirements) { + this.amount = amount; + this.actions = actions; + this.requirements = requirements; + } + + public int amountOfWater() { + return amount; + } + + public void triggerActions(Context context) { + ActionManager.trigger(context, actions); + } + + public boolean checkRequirements(Context context) { + return RequirementManager.isSatisfied(context, requirements); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/core/water/FillMethod.java similarity index 72% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java rename to api/src/main/java/net/momirealms/customcrops/api/core/water/FillMethod.java index df621f45a..0bc43f108 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/water/FillMethod.java @@ -15,16 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.item.water; +package net.momirealms.customcrops.api.core.water; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; -public class PositiveFillMethod extends AbstractFillMethod { +public class FillMethod extends AbstractMethod { private final String id; - public PositiveFillMethod(String id, int amount, Action[] actions, Requirement[] requirements) { + public FillMethod(String id, int amount, Action[] actions, Requirement[] requirements) { super(amount, actions, requirements); this.id = id; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/core/water/WateringMethod.java similarity index 76% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java rename to api/src/main/java/net/momirealms/customcrops/api/core/water/WateringMethod.java index adbef453a..a54918745 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/water/WateringMethod.java @@ -15,20 +15,29 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.item.water; +package net.momirealms.customcrops.api.core.water; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.requirement.Requirement; +import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; -public class PassiveFillMethod extends AbstractFillMethod { +public class WateringMethod extends AbstractMethod { private final String used; private final int usedAmount; private final String returned; private final int returnedAmount; - public PassiveFillMethod(String used, int usedAmount, @Nullable String returned, int returnedAmount, int amount, Action[] actions, Requirement[] requirements) { + public WateringMethod( + String used, + int usedAmount, + @Nullable String returned, + int returnedAmount, + int amount, + Action[] actions, + Requirement[] requirements + ) { super(amount, actions, requirements); this.used = used; this.returned = returned; diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/BlockPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java similarity index 68% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/world/BlockPos.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java index 10e29bc17..c3bf17a03 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/BlockPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java @@ -15,9 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.world; - -import java.util.Objects; +package net.momirealms.customcrops.api.core.world; public class BlockPos { @@ -31,31 +29,27 @@ public BlockPos(int x, int y, int z) { this.position = ((x & 0xF) << 28) | ((z & 0xF) << 24) | (y & 0xFFFFFF); } - public static BlockPos getByLocation(SimpleLocation location) { - return new BlockPos(location.getX() % 16, location.getY(), location.getZ() % 16); + public static BlockPos fromPos3(Pos3 location) { + return new BlockPos(location.x() % 16, location.y(), location.z() % 16); } - public SimpleLocation getLocation(String world, ChunkPos coordinate) { - return new SimpleLocation(world, coordinate.x() * 16 + getX(), getY(), coordinate.z() * 16 + getZ()); + public Pos3 toPos3(ChunkPos coordinate) { + return new Pos3(coordinate.x() * 16 + x(), y(), coordinate.z() * 16 + z()); } - public int getPosition() { + public int position() { return position; } - public int getX() { + public int x() { return (position >> 28) & 0xF; } - public int getZ() { + public int z() { return (position >> 24) & 0xF; } - public int getSectionID() { - return (int) Math.floor((double) getY() / 16); - } - - public int getY() { + public int y() { int y = position & 0xFFFFFF; if ((y & 0x800000) != 0) { y |= 0xFF000000; @@ -63,6 +57,10 @@ public int getY() { return y; } + public int sectionID() { + return (int) Math.floor((double) y() / 16); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -73,15 +71,15 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(position); + return Math.abs(position); } @Override public String toString() { return "BlockPos{" + - "x=" + getX() + - "y=" + getY() + - "z=" + getZ() + + "x=" + x() + + "y=" + y() + + "z=" + z() + '}'; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java similarity index 77% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkPos.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java index 511e46c7b..fe43a88ff 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.world; +package net.momirealms.customcrops.api.core.world; import org.bukkit.Chunk; import org.jetbrains.annotations.NotNull; @@ -26,17 +26,21 @@ public static ChunkPos of(int x, int z) { return new ChunkPos(x, z); } - public static ChunkPos getByString(String coordinate) { + public static ChunkPos fromString(String coordinate) { String[] split = coordinate.split(",", 2); try { int x = Integer.parseInt(split[0]); int z = Integer.parseInt(split[1]); return new ChunkPos(x, z); + } catch (NumberFormatException e) { + throw new RuntimeException(e); } - catch (NumberFormatException e) { - e.printStackTrace(); - return null; - } + } + + public static ChunkPos fromPos3(Pos3 pos3) { + int chunkX = (int) Math.floor((double) pos3.x() / 16.0); + int chunkZ = (int) Math.floor((double) pos3.z() / 16.0); + return ChunkPos.of(chunkX, chunkZ); } @Override @@ -64,16 +68,16 @@ public boolean equals(Object obj) { } @NotNull - public RegionPos getRegionPos() { + public RegionPos toRegionPos() { return RegionPos.getByChunkPos(this); } @NotNull - public static ChunkPos getByBukkitChunk(@NotNull Chunk chunk) { + public static ChunkPos fromBukkitChunk(@NotNull Chunk chunk) { return new ChunkPos(chunk.getX(), chunk.getZ()); } - public String getAsString() { + public String asString() { return x + "," + z; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java new file mode 100644 index 000000000..6b1dec8f0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java @@ -0,0 +1,15 @@ +package net.momirealms.customcrops.api.core.world; + +import com.flowpowered.nbt.CompoundMap; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import org.jetbrains.annotations.NotNull; + +public interface CustomCropsBlockState extends DataBlock { + + @NotNull + CustomCropsBlock type(); + + static CustomCropsBlockState create(CustomCropsBlock owner, CompoundMap compoundMap) { + return new CustomCropsBlockStateImpl(owner, compoundMap); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockStateImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockStateImpl.java new file mode 100644 index 000000000..5dd6a2d9b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockStateImpl.java @@ -0,0 +1,52 @@ +package net.momirealms.customcrops.api.core.world; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.Tag; +import net.momirealms.customcrops.api.core.SynchronizedCompoundMap; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import org.jetbrains.annotations.NotNull; + +public class CustomCropsBlockStateImpl implements CustomCropsBlockState { + + private final SynchronizedCompoundMap compoundMap; + private final CustomCropsBlock owner; + + protected CustomCropsBlockStateImpl(CustomCropsBlock owner, CompoundMap compoundMap) { + this.compoundMap = new SynchronizedCompoundMap(compoundMap); + this.owner = owner; + } + + @NotNull + @Override + public CustomCropsBlock type() { + return owner; + } + + @Override + public Tag set(String key, Tag tag) { + return compoundMap.put(key, tag); + } + + @Override + public Tag get(String key) { + return compoundMap.get(key); + } + + @Override + public Tag remove(String key) { + return compoundMap.remove(key); + } + + @Override + public SynchronizedCompoundMap compoundMap() { + return compoundMap; + } + + @Override + public String toString() { + return "CustomCropsBlock{" + + "Type{" + owner.type().asString() + + "}, " + compoundMap + + '}'; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java new file mode 100644 index 000000000..9d7894c0d --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) <2022> + * + * 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.core.world; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Optional; +import java.util.PriorityQueue; +import java.util.Set; +import java.util.stream.Stream; + +public interface CustomCropsChunk { + + /** + * Set if the chunk can be force loaded. + * If a chunk is force loaded, no one can unload it unless you set force load to false. + * This can prevent CustomCrops from unloading the chunks on {@link org.bukkit.event.world.ChunkUnloadEvent} + * + * This value will not be persistently stored. Please use {@link org.bukkit.World#setChunkForceLoaded(int, int, boolean)} + * if you want to force a chunk loaded. + * + * @param forceLoad force loaded + */ + void setForceLoaded(boolean forceLoad); + + /** + * Indicates whether the chunk is force loaded + * + * @return force loaded or not + */ + boolean isForceLoaded(); + + /** + * Loads the chunk to cache and participate in the mechanism of the plugin. + * + * @param loadBukkitChunk whether to load Bukkit chunks temporarily if it's not loaded + */ + void load(boolean loadBukkitChunk); + + /** + * Unloads the chunk. Lazy refer to those chunks that will be delayed for unloading. + * Recently unloaded chunks are likely to be loaded again soon. + * + * @param lazy delay unload or not + */ + void unload(boolean lazy); + + /** + * Unloads the chunk if it is a lazy chunk + */ + void unloadLazy(); + + /** + * Indicates whether the chunk is in lazy state + * + * @return lazy or not + */ + boolean isLazy(); + + /** + * Indicates whether the chunk is loaded + * + * @return loaded or not + */ + boolean isLoaded(); + + /** + * Get the world associated with the chunk + * + * @return CustomCrops world + */ + CustomCropsWorld getWorld(); + + /** + * Get the position of the chunk + * + * @return chunk position + */ + ChunkPos chunkPos(); + + /** + * Do second timer + */ + void timer(); + + /** + * Get the unloaded time in seconds + * This value would increase if the chunk is lazy + * + * @return the unloaded time + */ + int unloadedSeconds(); + + /** + * Set the unloaded seconds + * + * @param unloadedSeconds unloadedSeconds + */ + void unloadedSeconds(int unloadedSeconds); + + /** + * Get the last loaded time + * + * @return last loaded time + */ + long lastLoadedTime(); + + /** + * Set the last loaded time to current time + */ + void updateLastLoadedTime(); + + /** + * Get the loaded time in seconds + * + * @return loaded time + */ + int loadedMilliSeconds(); + + /** + * Get block data at a certain location + * + * @param location location + * @return block data + */ + @NotNull + Optional getBlockState(Pos3 location); + + /** + * Remove any block data from a certain location + * + * @param location location + * @return block data + */ + @NotNull + Optional removeBlockState(Pos3 location); + + /** + * Add a custom block data at a certain location + * + * @param block block to add + * @return the previous block data + */ + @NotNull + Optional addBlockState(Pos3 location, CustomCropsBlockState block); + + /** + * Get CustomCrops sections + * + * @return sections + */ + @NotNull + Stream sectionsToSave(); + + /** + * Get section by ID + * + * @param sectionID id + * @return section + */ + @NotNull + Optional getLoadedSection(int sectionID); + + CustomCropsSection getSection(int sectionID); + + Collection sections(); + + Optional removeSection(int sectionID); + + void resetUnloadedSeconds(); + + boolean canPrune(); + + boolean isOfflineTaskNotified(); + + PriorityQueue tickTaskQueue(); + + Set tickedBlocks(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java new file mode 100644 index 000000000..77fc3aa12 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java @@ -0,0 +1,288 @@ +package net.momirealms.customcrops.api.core.world; + +import net.momirealms.customcrops.common.util.RandomUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Stream; + +public class CustomCropsChunkImpl implements CustomCropsChunk { + + private final CustomCropsWorld world; + private final ChunkPos chunkPos; + private final ConcurrentHashMap loadedSections; + private final PriorityQueue queue; + private final Set tickedBlocks; + private long lastLoadedTime; + private int loadedSeconds; + private int unloadedSeconds; + private boolean notified; + private boolean isLoaded; + private boolean forceLoad; + + protected CustomCropsChunkImpl(CustomCropsWorld world, ChunkPos chunkPos) { + this.world = world; + this.chunkPos = chunkPos; + this.loadedSections = new ConcurrentHashMap<>(16); + this.queue = new PriorityQueue<>(); + this.unloadedSeconds = 0; + this.tickedBlocks = Collections.synchronizedSet(new HashSet<>()); + this.updateLastLoadedTime(); + this.notified = true; + this.isLoaded = false; + } + + protected CustomCropsChunkImpl( + CustomCropsWorld world, + ChunkPos chunkPos, + int loadedSeconds, + long lastLoadedTime, + ConcurrentHashMap loadedSections, + PriorityQueue queue, + HashSet tickedBlocks + ) { + this.world = world; + this.chunkPos = chunkPos; + this.loadedSections = loadedSections; + this.lastLoadedTime = lastLoadedTime; + this.loadedSeconds = loadedSeconds; + this.queue = queue; + this.unloadedSeconds = 0; + this.tickedBlocks = Collections.synchronizedSet(tickedBlocks); + } + + @Override + public void setForceLoaded(boolean forceLoad) { + this.forceLoad = forceLoad; + } + + @Override + public boolean isForceLoaded() { + return this.forceLoad; + } + + @Override + public void load(boolean loadBukkitChunk) { + if (!isLoaded()) { + if (((CustomCropsWorldImpl) world).loadChunk(this)) { + this.isLoaded = true; + } + if (loadBukkitChunk && !this.world.bukkitWorld().isChunkLoaded(chunkPos.x(), chunkPos.z())) { + this.world.bukkitWorld().getChunkAt(chunkPos.x(), chunkPos.z()); + } + } + } + + @Override + public void unload(boolean lazy) { + if (isLoaded() && !isForceLoaded()) { + if (((CustomCropsWorldImpl) world).unloadChunk(this, lazy)) { + this.isLoaded = false; + } + } + } + + @Override + public void unloadLazy() { + if (!isLoaded() && isLazy()) { + ((CustomCropsWorldImpl) world).unloadLazyChunk(chunkPos); + } + } + + @Override + public boolean isLazy() { + return ((CustomCropsWorldImpl) world).getLazyChunk(chunkPos) == this; + } + + @Override + public boolean isLoaded() { + return this.isLoaded; + } + + @Override + public CustomCropsWorld getWorld() { + return world; + } + + @Override + public ChunkPos chunkPos() { + return chunkPos; + } + + @Override + public void timer() { + WorldSetting setting = world.setting(); + int interval = setting.minTickUnit(); + this.loadedSeconds++; + // if loadedSeconds reach another recycle, rearrange the tasks + if (this.loadedSeconds >= interval) { + this.loadedSeconds = 0; + this.tickedBlocks.clear(); + this.queue.clear(); + this.arrangeTasks(interval); + } + scheduledTick(); + randomTick(setting.randomTickSpeed()); + } + + private void arrangeTasks(int unit) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for (CustomCropsSection section : loadedSections.values()) { + for (Map.Entry entry : section.blockMap().entrySet()) { + this.queue.add(new DelayedTickTask( + random.nextInt(0, unit), + entry.getKey() + )); + this.tickedBlocks.add(entry.getKey()); + } + } + } + + private void scheduledTick() { + while (!queue.isEmpty() && queue.peek().getTime() <= loadedSeconds) { + DelayedTickTask task = queue.poll(); + if (task != null) { + BlockPos pos = task.blockPos(); + CustomCropsSection section = loadedSections.get(pos.sectionID()); + if (section != null) { + Optional block = section.getBlockState(pos); + block.ifPresent(state -> state.type().scheduledTick(state, world, pos.toPos3(chunkPos))); + } + } + } + } + + private void randomTick(int randomTickSpeed) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for (CustomCropsSection section : loadedSections.values()) { + int sectionID = section.getSectionID(); + int baseY = sectionID * 16; + for (int i = 0; i < randomTickSpeed; i++) { + int x = random.nextInt(16); + int y = random.nextInt(16) + baseY; + int z = random.nextInt(16); + BlockPos pos = new BlockPos(x,y,z); + Optional block = section.getBlockState(pos); + block.ifPresent(state -> state.type().randomTick(state, world, pos.toPos3(chunkPos))); + } + } + } + + @Override + public int unloadedSeconds() { + return unloadedSeconds; + } + + @Override + public void unloadedSeconds(int unloadedSeconds) { + this.unloadedSeconds = unloadedSeconds; + } + + @Override + public long lastLoadedTime() { + return lastLoadedTime; + } + + @Override + public void updateLastLoadedTime() { + this.lastLoadedTime = System.currentTimeMillis(); + } + + @Override + public int loadedMilliSeconds() { + return (int) (System.currentTimeMillis() - lastLoadedTime); + } + + @NotNull + @Override + public Optional getBlockState(Pos3 location) { + BlockPos pos = BlockPos.fromPos3(location); + return getLoadedSection(pos.sectionID()).flatMap(section -> section.getBlockState(pos)); + } + + @NotNull + @Override + public Optional removeBlockState(Pos3 location) { + BlockPos pos = BlockPos.fromPos3(location); + return getLoadedSection(pos.sectionID()).flatMap(section -> section.removeBlockState(pos)); + } + + @NotNull + @Override + public Optional addBlockState(Pos3 location, CustomCropsBlockState block) { + BlockPos pos = BlockPos.fromPos3(location); + CustomCropsSection section = getSection(pos.sectionID()); + this.arrangeScheduledTickTaskForNewBlock(pos); + return section.addBlockState(pos, block); + } + + @NotNull + @Override + public Stream sectionsToSave() { + return loadedSections.values().stream().filter(section -> !section.canPrune()); + } + + @NotNull + @Override + public Optional getLoadedSection(int sectionID) { + return Optional.ofNullable(loadedSections.get(sectionID)); + } + + @Override + public CustomCropsSection getSection(int sectionID) { + return getLoadedSection(sectionID).orElseGet(() -> { + CustomCropsSection section = new CustomCropsSectionImpl(sectionID); + this.loadedSections.put(sectionID, section); + return section; + }); + } + + @Override + public Collection sections() { + return loadedSections.values(); + } + + @Override + public Optional removeSection(int sectionID) { + return Optional.ofNullable(loadedSections.remove(sectionID)); + } + + @Override + public void resetUnloadedSeconds() { + this.unloadedSeconds = 0; + this.notified = false; + } + + @Override + public boolean canPrune() { + return loadedSections.isEmpty(); + } + + @Override + public boolean isOfflineTaskNotified() { + return notified; + } + + @Override + public PriorityQueue tickTaskQueue() { + return queue; + } + + @Override + public Set tickedBlocks() { + return tickedBlocks; + } + + private void arrangeScheduledTickTaskForNewBlock(BlockPos pos) { + WorldSetting setting = world.setting(); + if (!tickedBlocks.contains(pos)) { + tickedBlocks.add(pos); + int random = RandomUtils.generateRandomInt(0, setting.minTickUnit() - 1); + if (random > loadedSeconds) { + queue.add(new DelayedTickTask(random, pos)); + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakBlockWrapper.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java similarity index 58% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakBlockWrapper.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java index 0851a6ee5..d8f830215 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakBlockWrapper.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java @@ -15,22 +15,32 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.item.function.wrapper; +package net.momirealms.customcrops.api.core.world; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class BreakBlockWrapper extends BreakWrapper { +import java.util.Map; - private final Block brokenBlock; +public interface CustomCropsRegion { - public BreakBlockWrapper(Player player, Block brokenBlock) { - super(player, brokenBlock.getLocation()); - this.brokenBlock = brokenBlock; - } + boolean isLoaded(); - public Block getBrokenBlock() { - return brokenBlock; - } -} + void unload(); + + void load(); + + @NotNull + CustomCropsWorld getWorld(); + + byte[] getCachedChunkBytes(ChunkPos pos); + + RegionPos regionPos(); + boolean removeCachedChunk(ChunkPos pos); + + void setCachedChunk(ChunkPos pos, byte[] data); + + Map dataToSave(); + + boolean canPrune(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java new file mode 100644 index 000000000..a6ebed2e9 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java @@ -0,0 +1,86 @@ +package net.momirealms.customcrops.api.core.world; + +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class CustomCropsRegionImpl implements CustomCropsRegion { + + private final CustomCropsWorld world; + private final RegionPos regionPos; + private final ConcurrentHashMap cachedChunks; + private boolean isLoaded = false; + + protected CustomCropsRegionImpl(CustomCropsWorld world, RegionPos regionPos) { + this.world = world; + this.cachedChunks = new ConcurrentHashMap<>(); + this.regionPos = regionPos; + } + + protected CustomCropsRegionImpl(CustomCropsWorld world, RegionPos regionPos, ConcurrentHashMap cachedChunks) { + this.world = world; + this.regionPos = regionPos; + this.cachedChunks = cachedChunks; + } + + @Override + public boolean isLoaded() { + return isLoaded; + } + + @Override + public void unload() { + if (this.isLoaded) { + if (((CustomCropsWorldImpl) world).unloadRegion(this)) { + this.isLoaded = false; + } + } + } + + @Override + public void load() { + if (!this.isLoaded) { + if (((CustomCropsWorldImpl) world).loadRegion(this)) { + this.isLoaded = true; + } + } + } + + @NotNull + @Override + public CustomCropsWorld getWorld() { + return this.world; + } + + @Override + public byte[] getCachedChunkBytes(ChunkPos pos) { + return this.cachedChunks.get(pos); + } + + @Override + public RegionPos regionPos() { + return this.regionPos; + } + + @Override + public boolean removeCachedChunk(ChunkPos pos) { + return cachedChunks.remove(pos) != null; + } + + @Override + public void setCachedChunk(ChunkPos pos, byte[] data) { + this.cachedChunks.put(pos, data); + } + + @Override + public Map dataToSave() { + return new HashMap<>(cachedChunks); + } + + @Override + public boolean canPrune() { + return cachedChunks.isEmpty(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java new file mode 100644 index 000000000..e44d4a492 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) <2022> + * + * 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.core.world; + +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +public interface CustomCropsSection { + + static CustomCropsSection create(int sectionID) { + return new CustomCropsSectionImpl(sectionID); + } + + static CustomCropsSection restore(int sectionID, ConcurrentHashMap blocks) { + return new CustomCropsSectionImpl(sectionID, blocks); + } + + int getSectionID(); + + @NotNull + Optional getBlockState(BlockPos pos); + + @NotNull + Optional removeBlockState(BlockPos pos); + + @NotNull + Optional addBlockState(BlockPos pos, CustomCropsBlockState block); + + boolean canPrune(); + + CustomCropsBlockState[] blocks(); + + Map blockMap(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSectionImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSectionImpl.java new file mode 100644 index 000000000..891afa0d5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSectionImpl.java @@ -0,0 +1,61 @@ +package net.momirealms.customcrops.api.core.world; + +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +public class CustomCropsSectionImpl implements CustomCropsSection { + + private final int sectionID; + private final ConcurrentHashMap blocks; + + protected CustomCropsSectionImpl(int sectionID) { + this.sectionID = sectionID; + this.blocks = new ConcurrentHashMap<>(); + } + + protected CustomCropsSectionImpl(int sectionID, ConcurrentHashMap blocks) { + this.sectionID = sectionID; + this.blocks = blocks; + } + + @Override + public int getSectionID() { + return sectionID; + } + + @NotNull + @Override + public Optional getBlockState(BlockPos pos) { + return Optional.ofNullable(blocks.get(pos)); + } + + @NotNull + @Override + public Optional removeBlockState(BlockPos pos) { + return Optional.ofNullable(blocks.remove(pos)); + } + + @NotNull + @Override + public Optional addBlockState(BlockPos pos, CustomCropsBlockState block) { + return Optional.ofNullable(blocks.put(pos, block)); + } + + @Override + public boolean canPrune() { + return blocks.isEmpty(); + } + + @Override + public CustomCropsBlockState[] blocks() { + return blocks.values().toArray(new CustomCropsBlockState[0]); + } + + @Override + public Map blockMap() { + return blocks; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java new file mode 100644 index 000000000..f4438ec9c --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) <2022> + * + * 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.core.world; + +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.world.adaptor.WorldAdaptor; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Optional; +import java.util.PriorityQueue; +import java.util.concurrent.ConcurrentHashMap; + +public interface CustomCropsWorld { + + /** + * Create a CustomCrops world + * + * @param world world instance + * @param adaptor the world adaptor + * @return CustomCrops world + * @param world type + */ + static CustomCropsWorld create(W world, WorldAdaptor adaptor) { + return new CustomCropsWorldImpl<>(world, adaptor); + } + + /** + * Create a new CustomCropsChunk associated with this world + * + * @param pos the position of the chunk + * @return the created chunk + */ + default CustomCropsChunk createChunk(ChunkPos pos) { + return new CustomCropsChunkImpl(this, pos); + } + + default CustomCropsChunk restoreChunk( + ChunkPos pos, + int loadedSeconds, + long lastLoadedTime, + ConcurrentHashMap loadedSections, + PriorityQueue queue, + HashSet tickedBlocks + ) { + return new CustomCropsChunkImpl(this, pos, loadedSeconds, lastLoadedTime, loadedSections, queue, tickedBlocks); + } + + default CustomCropsRegion createRegion(RegionPos pos) { + return new CustomCropsRegionImpl(this, pos); + } + + default CustomCropsRegion restoreRegion(RegionPos pos, ConcurrentHashMap cachedChunks) { + return new CustomCropsRegionImpl(this, pos, cachedChunks); + } + + WorldAdaptor adaptor(); + + WorldExtraData extraData(); + + boolean testChunkLimitation(Pos3 pos3, Class clazz, int amount); + + boolean doesChunkHaveBlock(Pos3 pos3, Class clazz); + + int getChunkBlockAmount(Pos3 pos3, Class clazz); + + /** + * Get the state of the block at a certain location + * + * @param location location of the block state + * @return the optional block state + */ + @NotNull + Optional getBlockState(Pos3 location); + + /** + * Remove the block state from a certain location + * + * @param location the location of the block state + * @return the optional removed state + */ + @NotNull + Optional removeBlockState(Pos3 location); + + /** + * Add block state at the certain location + * + * @param location location of the state + * @param block block state to add + * @return the optional previous state + */ + @NotNull + Optional addBlockState(Pos3 location, CustomCropsBlockState block); + + /** + * Save the world to file + */ + void save(); + + /** + * Set if the ticking task is ongoing + * + * @param tick ongoing or not + */ + void setTicking(boolean tick); + + /** + * Get the world associated with this world + * + * @return Bukkit world + */ + W world(); + + World bukkitWorld(); + + String worldName(); + + /** + * Get the settings of the world + * + * @return the setting + */ + @NotNull + WorldSetting setting(); + + /** + * Set the settings of the world + * + * @param setting setting + */ + void setting(WorldSetting setting); + + /* + * Chunks + */ + boolean isChunkLoaded(ChunkPos pos); + + /** + * Get loaded chunk from cache + * + * @param chunkPos the position of the chunk + * @return the optional loaded chunk + */ + @NotNull + Optional getLoadedChunk(ChunkPos chunkPos); + + /** + * Get chunk from cache or file + * + * @param chunkPos the position of the chunk + * @return the optional chunk + */ + @NotNull + Optional getChunk(ChunkPos chunkPos); + + /** + * Get chunk from cache or file, create if not found + * + * @param chunkPos the position of the chunk + * @return the chunk + */ + @NotNull + CustomCropsChunk getOrCreateChunk(ChunkPos chunkPos); + + /** + * Check if a region is loaded + * + * @param regionPos the position of the region + * @return loaded or not + */ + boolean isRegionLoaded(RegionPos regionPos); + + /** + * Get the loaded region, empty if not loaded + * + * @param regionPos position of the region + * @return the optional loaded region + */ + @NotNull + Optional getLoadedRegion(RegionPos regionPos); + + /** + * Get the region from cache or file + * + * @param regionPos position of the region + * @return the optional region + */ + @NotNull + Optional getRegion(RegionPos regionPos); + + /** + * Get the region from cache or file, create if not found + * + * @param regionPos position of the region + * @return the region + */ + @NotNull + CustomCropsRegion getOrCreateRegion(RegionPos regionPos); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java new file mode 100644 index 000000000..1c8744cac --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java @@ -0,0 +1,474 @@ +package net.momirealms.customcrops.api.core.world; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.world.adaptor.WorldAdaptor; +import net.momirealms.customcrops.common.helper.VersionHelper; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerAdapter; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class CustomCropsWorldImpl implements CustomCropsWorld { + + private final ConcurrentHashMap loadedChunks = new ConcurrentHashMap<>(512); + private final ConcurrentHashMap lazyChunks = new ConcurrentHashMap<>(128); + private final ConcurrentHashMap loadedRegions = new ConcurrentHashMap<>(128); + private final WeakReference world; + private final WeakReference bukkitWorld; + private final String worldName; + private long currentMinecraftDay; + private int regionTimer; + private SchedulerTask tickTask; + private WorldSetting setting; + private final WorldAdaptor adaptor; + private final WorldExtraData extraData; + + public CustomCropsWorldImpl(W world, WorldAdaptor adaptor) { + this.world = new WeakReference<>(world); + this.worldName = adaptor.getName(world); + this.bukkitWorld = new WeakReference<>(Bukkit.getWorld(worldName)); + this.regionTimer = 0; + this.adaptor = adaptor; + this.extraData = adaptor.loadExtraData(world); + this.currentMinecraftDay = (int) (adaptor.getWorldFullTime(world) / 24_000); + } + + @Override + public WorldAdaptor adaptor() { + return adaptor; + } + + @NotNull + @Override + public WorldExtraData extraData() { + return extraData; + } + + @Override + public boolean testChunkLimitation(Pos3 pos3, Class clazz, int amount) { + Optional optional = getChunk(pos3.toChunkPos()); + if (optional.isPresent()) { + int i = 0; + CustomCropsChunk chunk = optional.get(); + for (CustomCropsSection section : chunk.sections()) { + for (CustomCropsBlockState state : section.blockMap().values()) { + if (clazz.isAssignableFrom(state.type().getClass())) { + i++; + if (i >= amount) { + return false; + } + } + } + } + return true; + } else { + return false; + } + } + + @Override + public boolean doesChunkHaveBlock(Pos3 pos3, Class clazz) { + Optional optional = getChunk(pos3.toChunkPos()); + if (optional.isPresent()) { + CustomCropsChunk chunk = optional.get(); + for (CustomCropsSection section : chunk.sections()) { + for (CustomCropsBlockState state : section.blockMap().values()) { + if (clazz.isAssignableFrom(state.type().getClass())) { + return true; + } + } + } + } + return false; + } + + @Override + public int getChunkBlockAmount(Pos3 pos3, Class clazz) { + Optional optional = getChunk(pos3.toChunkPos()); + if (optional.isPresent()) { + int i = 0; + CustomCropsChunk chunk = optional.get(); + for (CustomCropsSection section : chunk.sections()) { + for (CustomCropsBlockState state : section.blockMap().values()) { + if (clazz.isAssignableFrom(state.type().getClass())) { + i++; + } + } + } + return i; + } else { + return 0; + } + } + + @NotNull + @Override + public Optional getBlockState(Pos3 location) { + ChunkPos pos = location.toChunkPos(); + Optional chunk = getChunk(pos); + if (chunk.isEmpty()) { + return Optional.empty(); + } else { + CustomCropsChunk customChunk = chunk.get(); + // to let the bukkit system trigger the ChunkUnloadEvent later + customChunk.load(true); + return customChunk.getBlockState(location); + } + } + + @NotNull + @Override + public Optional removeBlockState(Pos3 location) { + ChunkPos pos = location.toChunkPos(); + Optional chunk = getChunk(pos); + if (chunk.isEmpty()) { + return Optional.empty(); + } else { + CustomCropsChunk customChunk = chunk.get(); + // to let the bukkit system trigger the ChunkUnloadEvent later + customChunk.load(true); + return customChunk.removeBlockState(location); + } + } + + @NotNull + @Override + public Optional addBlockState(Pos3 location, CustomCropsBlockState block) { + ChunkPos pos = location.toChunkPos(); + CustomCropsChunk chunk = getOrCreateChunk(pos); + // to let the bukkit system trigger the ChunkUnloadEvent later + chunk.load(true); + return chunk.addBlockState(location, block); + } + + @Override + public void save() { + long time1 = System.currentTimeMillis(); + this.adaptor.saveExtraData(this); + for (CustomCropsChunk chunk : loadedChunks.values()) { + this.adaptor.saveChunk(this, chunk); + } + for (CustomCropsChunk chunk : lazyChunks.values()) { + this.adaptor.saveChunk(this, chunk); + } + for (CustomCropsRegion region : loadedRegions.values()) { + this.adaptor.saveRegion(this, region); + } + long time2 = System.currentTimeMillis(); + BukkitCustomCropsPlugin.getInstance().debug("Took " + (time2-time1) + "ms to save world " + worldName + ". Saved " + (lazyChunks.size() + loadedChunks.size()) + " chunks."); + } + + @Override + public void setTicking(boolean tick) { + if (tick) { + if (this.tickTask == null || this.tickTask.isCancelled()) + this.tickTask = BukkitCustomCropsPlugin.getInstance().getScheduler().asyncRepeating(this::timer, 1, 1, TimeUnit.SECONDS); + } else { + if (this.tickTask != null && !this.tickTask.isCancelled()) + this.tickTask.cancel(); + } + } + + private void timer() { + saveLazyChunks(); + saveLazyRegions(); + if (isANewDay()) { + updateSeasonAndDate(); + } + if (setting().enableScheduler()) { + tickChunks(); + } + } + + private void tickChunks() { + if (VersionHelper.isFolia()) { + SchedulerAdapter scheduler = BukkitCustomCropsPlugin.getInstance().getScheduler(); + for (CustomCropsChunk chunk : loadedChunks.values()) { + scheduler.sync().run(chunk::timer, bukkitWorld(), chunk.chunkPos().x(), chunk.chunkPos().z()); + } + } else { + for (CustomCropsChunk chunk : loadedChunks.values()) { + chunk.timer(); + } + } + } + + private void updateSeasonAndDate() { + int date = extraData().getDate(); + date++; + if (date > setting().seasonDuration()) { + Season season = extraData().getSeason().getNextSeason(); + extraData().setSeason(season); + extraData().setDate(1); + } else { + extraData().setDate(date); + } + } + + private boolean isANewDay() { + long currentDay = bukkitWorld().getFullTime() / 24_000; + if (currentDay != currentMinecraftDay) { + currentMinecraftDay = currentDay; + return true; + } + return false; + } + + private void saveLazyRegions() { + this.regionTimer++; + if (this.regionTimer >= 600) { + this.regionTimer = 0; + ArrayList removed = new ArrayList<>(); + for (Map.Entry entry : loadedRegions.entrySet()) { + if (shouldUnloadRegion(entry.getKey())) { + removed.add(entry.getValue()); + } + } + for (CustomCropsRegion region : removed) { + region.unload(); + } + } + } + + private void saveLazyChunks() { + ArrayList chunksToSave = new ArrayList<>(); + for (Map.Entry lazyEntry : this.lazyChunks.entrySet()) { + CustomCropsChunk chunk = lazyEntry.getValue(); + int sec = chunk.unloadedSeconds() + 1; + if (sec >= 30) { + chunksToSave.add(chunk); + } else { + chunk.unloadedSeconds(sec); + } + } + for (CustomCropsChunk chunk : chunksToSave) { + unloadLazyChunk(chunk.chunkPos()); + } + } + + @Override + public W world() { + return world.get(); + } + + @Override + public World bukkitWorld() { + return bukkitWorld.get(); + } + + @Override + public String worldName() { + return worldName; + } + + @Override + public @NotNull WorldSetting setting() { + return setting; + } + + @Override + public void setting(WorldSetting setting) { + this.setting = setting; + } + + /* + * Chunks + */ + @Nullable + public CustomCropsChunk removeLazyChunk(ChunkPos chunkPos) { + return this.lazyChunks.remove(chunkPos); + } + + @Nullable + public CustomCropsChunk getLazyChunk(ChunkPos chunkPos) { + return this.lazyChunks.get(chunkPos); + } + + @Override + public boolean isChunkLoaded(ChunkPos pos) { + return this.loadedChunks.containsKey(pos); + } + + public boolean loadChunk(CustomCropsChunk chunk) { + Optional previousChunk = getLoadedChunk(chunk.chunkPos()); + if (previousChunk.isPresent()) { + BukkitCustomCropsPlugin.getInstance().debug("Chunk " + chunk.chunkPos() + " already loaded."); + if (previousChunk.get() != chunk) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to load the chunk. There is already a different chunk instance with the same coordinates in the cache. " + chunk.chunkPos()); + return false; + } + return true; + } + this.loadedChunks.put(chunk.chunkPos(), chunk); + this.lazyChunks.remove(chunk.chunkPos()); + return true; + } + + @ApiStatus.Internal + public boolean unloadChunk(CustomCropsChunk chunk, boolean lazy) { + ChunkPos pos = chunk.chunkPos(); + Optional previousChunk = getLoadedChunk(chunk.chunkPos()); + if (previousChunk.isPresent()) { + if (previousChunk.get() != chunk) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to remove the chunk. The provided chunk instance is inconsistent with the one in the cache. " + chunk.chunkPos()); + return false; + } + } else { + return false; + } + this.loadedChunks.remove(chunk.chunkPos()); + chunk.updateLastLoadedTime(); + if (lazy) { + this.lazyChunks.put(pos, chunk); + } else { + this.adaptor.saveChunk(this, chunk); + } + return true; + } + + @ApiStatus.Internal + public boolean unloadChunk(ChunkPos pos, boolean lazy) { + CustomCropsChunk removed = this.loadedChunks.remove(pos); + if (removed != null) { + removed.updateLastLoadedTime(); + if (lazy) { + this.lazyChunks.put(pos, removed); + } else { + this.adaptor.saveChunk(this, removed); + } + return true; + } + return false; + } + + @ApiStatus.Internal + public boolean unloadLazyChunk(ChunkPos pos) { + CustomCropsChunk removed = this.lazyChunks.remove(pos); + if (removed != null) { + this.adaptor.saveChunk(this, removed); + return true; + } + return false; + } + + @NotNull + @Override + public Optional getLoadedChunk(ChunkPos chunkPos) { + return Optional.ofNullable(this.loadedChunks.get(chunkPos)); + } + + @NotNull + @Override + public Optional getChunk(ChunkPos chunkPos) { + return Optional.ofNullable(getLoadedChunk(chunkPos).orElseGet(() -> { + CustomCropsChunk chunk = getLazyChunk(chunkPos); + if (chunk != null) { + return chunk; + } + return this.adaptor.loadChunk(this, chunkPos, false); + })); + } + + @NotNull + @Override + public CustomCropsChunk getOrCreateChunk(ChunkPos chunkPos) { + return Objects.requireNonNull(getLoadedChunk(chunkPos).orElseGet(() -> { + CustomCropsChunk chunk = getLazyChunk(chunkPos); + if (chunk != null) { + return chunk; + } + return this.adaptor.loadChunk(this, chunkPos, true); + })); + } + + /* + * Regions + */ + @Override + public boolean isRegionLoaded(RegionPos pos) { + return this.loadedRegions.containsKey(pos); + } + + @ApiStatus.Internal + public boolean loadRegion(CustomCropsRegion region) { + Optional previousRegion = getLoadedRegion(region.regionPos()); + if (previousRegion.isPresent()) { + BukkitCustomCropsPlugin.getInstance().debug("Region " + region.regionPos() + " already loaded."); + if (previousRegion.get() != region) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to load the region. There is already a different region instance with the same coordinates in the cache. " + region.regionPos()); + return false; + } + return true; + } + this.loadedRegions.put(region.regionPos(), region); + return true; + } + + @NotNull + @Override + public Optional getLoadedRegion(RegionPos regionPos) { + return Optional.ofNullable(loadedRegions.get(regionPos)); + } + + @NotNull + @Override + public Optional getRegion(RegionPos regionPos) { + return Optional.ofNullable(getLoadedRegion(regionPos).orElse(adaptor.loadRegion(this, regionPos, false))); + } + + @NotNull + @Override + public CustomCropsRegion getOrCreateRegion(RegionPos regionPos) { + return Objects.requireNonNull(getLoadedRegion(regionPos).orElse(adaptor.loadRegion(this, regionPos, true))); + } + + private boolean shouldUnloadRegion(RegionPos regionPos) { + for (int chunkX = regionPos.x() * 32; chunkX < regionPos.x() * 32 + 32; chunkX++) { + for (int chunkZ = regionPos.z() * 32; chunkZ < regionPos.z() * 32 + 32; chunkZ++) { + // if a chunk is unloaded, then it should not be in the loaded chunks map + ChunkPos pos = ChunkPos.of(chunkX, chunkZ); + if (isChunkLoaded(pos) || this.lazyChunks.containsKey(pos)) { + return false; + } + } + } + return true; + } + + public boolean unloadRegion(CustomCropsRegion region) { + Optional previousRegion = getLoadedRegion(region.regionPos()); + if (previousRegion.isPresent()) { + if (previousRegion.get() != region) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to remove the region. The provided region instance is inconsistent with the one in the cache. " + region.regionPos()); + return false; + } + } else { + return false; + } + RegionPos regionPos = region.regionPos(); + for (int chunkX = regionPos.x() * 32; chunkX < regionPos.x() * 32 + 32; chunkX++) { + for (int chunkZ = regionPos.z() * 32; chunkZ < regionPos.z() * 32 + 32; chunkZ++) { + ChunkPos pos = ChunkPos.of(chunkX, chunkZ); + if (!unloadLazyChunk(pos)) { + unloadChunk(pos, false); + } + } + } + this.loadedRegions.remove(region.regionPos()); + this.adaptor.saveRegion(this, region); + return true; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java similarity index 65% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java index 0071623e1..f063ffb08 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java @@ -15,16 +15,23 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.item.fertilizer; +package net.momirealms.customcrops.api.core.world; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import com.flowpowered.nbt.Tag; +import net.momirealms.customcrops.api.core.SynchronizedCompoundMap; -public interface SoilRetain extends Fertilizer { +public interface DataBlock { + + Tag set(String key, Tag tag); + + Tag get(String key); + + Tag remove(String key); /** - * Get the chance of taking effect + * Get the data map * - * @return chance + * @return data map */ - double getChance(); + SynchronizedCompoundMap compoundMap(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/TickTask.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/DelayedTickTask.java similarity index 80% rename from plugin/src/main/java/net/momirealms/customcrops/scheduler/task/TickTask.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/DelayedTickTask.java index 273816e90..2526dfdf3 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/TickTask.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/DelayedTickTask.java @@ -15,25 +15,24 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.scheduler.task; +package net.momirealms.customcrops.api.core.world; -import net.momirealms.customcrops.api.mechanic.world.BlockPos; import org.jetbrains.annotations.NotNull; -public class TickTask implements Comparable { +public class DelayedTickTask implements Comparable { private static int taskID; private final int time; private final BlockPos blockPos; private final int id; - public TickTask(int time, BlockPos blockPos) { + public DelayedTickTask(int time, BlockPos blockPos) { this.time = time; this.blockPos = blockPos; this.id = taskID++; } - public BlockPos getChunkPos() { + public BlockPos blockPos() { return blockPos; } @@ -42,7 +41,7 @@ public int getTime() { } @Override - public int compareTo(@NotNull TickTask o) { + public int compareTo(@NotNull DelayedTickTask o) { if (this.time > o.time) { return 1; } else if (this.time < o.time) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/Pos3.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/Pos3.java new file mode 100644 index 000000000..4be100f3b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/Pos3.java @@ -0,0 +1,87 @@ +/* + * 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.core.world; + +import org.bukkit.Location; +import org.bukkit.World; + +public record Pos3(int x, int y, int z) { + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Pos3 other = (Pos3) obj; + if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) { + return false; + } + if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) { + return false; + } + if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 19 * hash + Long.hashCode(Double.doubleToLongBits(this.x)); + hash = 19 * hash + Long.hashCode(Double.doubleToLongBits(this.y)); + hash = 19 * hash + Long.hashCode(Double.doubleToLongBits(this.z)); + return hash; + } + + public static Pos3 from(Location location) { + return new Pos3(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + public Location toLocation(World world) { + return new Location(world, x, y, z); + } + + public Pos3 add(int x, int y, int z) { + return new Pos3(this.x + x, this.y + y, this.z + z); + } + + public ChunkPos toChunkPos() { + return ChunkPos.fromPos3(this); + } + + public int chunkX() { + return (int) Math.floor((double) this.x() / 16.0); + } + + public int chunkZ() { + return (int) Math.floor((double) this.z() / 16.0); + } + + @Override + public String toString() { + return "Pos3{" + + "x=" + x + + ", y=" + y + + ", z=" + z + + '}'; + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/RegionPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java similarity index 95% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/world/RegionPos.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java index 7b047634f..ac0cf690a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/RegionPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.world; +package net.momirealms.customcrops.api.core.world; import org.bukkit.Chunk; import org.jetbrains.annotations.NotNull; @@ -34,8 +34,7 @@ public static RegionPos getByString(String coordinate) { return new RegionPos(x, z); } catch (NumberFormatException e) { - e.printStackTrace(); - return null; + throw new RuntimeException("Invalid coordinate: " + coordinate); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/Season.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/Season.java new file mode 100644 index 000000000..f6a0398e2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/Season.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) <2022> + * + * 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.core.world; + +import net.momirealms.customcrops.common.locale.MessageConstants; +import net.momirealms.customcrops.common.locale.TranslationManager; + +import java.util.Optional; +import java.util.function.Supplier; + +public enum Season { + + SPRING(() -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.SEASON_SPRING.build().key())).orElse("Spring")), + SUMMER(() -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.SEASON_SUMMER.build().key())).orElse("Summer")), + AUTUMN(() -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.SEASON_AUTUMN.build().key())).orElse("Autumn")), + WINTER(() -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.SEASON_WINTER.build().key())).orElse("Winter")), + DISABLE(() -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.SEASON_DISABLE.build().key())).orElse("Disabled")); + + private final Supplier translationSupplier; + + Season(Supplier translationSupplier) { + this.translationSupplier = translationSupplier; + } + + public String translation() { + return translationSupplier.get(); + } + + public Season getNextSeason() { + return switch (this) { + case SPRING -> SUMMER; + case SUMMER -> AUTUMN; + case AUTUMN -> WINTER; + case WINTER -> SPRING; + default -> DISABLE; + }; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceFurnitureWrapper.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableChunk.java similarity index 67% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceFurnitureWrapper.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableChunk.java index 31c5c11dc..4b9d496b1 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceFurnitureWrapper.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableChunk.java @@ -15,14 +15,14 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.item.function.wrapper; +package net.momirealms.customcrops.api.core.world; -import org.bukkit.Location; -import org.bukkit.entity.Player; +import java.util.List; -public class PlaceFurnitureWrapper extends PlaceWrapper { +public record SerializableChunk(int x, int z, int loadedSeconds, long lastLoadedTime, + List sections, int[] queuedTasks, int[] ticked) { - public PlaceFurnitureWrapper(Player player, Location location, String id) { - super(player, location, id); + public boolean canPrune() { + return sections.isEmpty(); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/season/Season.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableSection.java similarity index 72% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/world/season/Season.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableSection.java index 187e1612a..5204eb2e3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/season/Season.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableSection.java @@ -15,16 +15,15 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.world.season; +package net.momirealms.customcrops.api.core.world; -public enum Season { +import com.flowpowered.nbt.CompoundTag; - SPRING, - SUMMER, - AUTUMN, - WINTER; +import java.util.List; - public Season getNextSeason() { - return Season.values()[(this.ordinal() + 1) % 4]; +public record SerializableSection(int sectionID, List blocks) { + + public boolean canPrune() { + return blocks.isEmpty(); } -} \ No newline at end of file +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java similarity index 69% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java index 51c97e198..ecfa0c7c2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java @@ -15,25 +15,43 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.world.level; +package net.momirealms.customcrops.api.core.world; import com.google.gson.annotations.SerializedName; -import net.momirealms.customcrops.api.mechanic.world.season.Season; +import org.jetbrains.annotations.Nullable; -public class WorldInfoData { +import java.util.HashMap; + +public class WorldExtraData { @SerializedName("season") private Season season; @SerializedName("date") private int date; + @SerializedName("extra") + private HashMap extra; - public WorldInfoData(Season season, int date) { + public WorldExtraData(Season season, int date) { this.season = season; this.date = date; + this.extra = new HashMap<>(); + } + + public static WorldExtraData empty() { + return new WorldExtraData(Season.SPRING, 1); + } + + public void addExtraData(String key, Object value) { + this.extra.put(key, value); + } + + public void removeExtraData(String key) { + this.extra.remove(key); } - public static WorldInfoData empty() { - return new WorldInfoData(Season.SPRING, 1); + @Nullable + public Object getExtraData(String key) { + return this.extra.get(key); } /** @@ -75,7 +93,7 @@ public void setDate(int date) { @Override public String toString() { - return "WorldInfoData{" + + return "WorldExtraData{" + "season=" + season + ", date=" + date + '}'; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java new file mode 100644 index 000000000..3905e5e93 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java @@ -0,0 +1,31 @@ +package net.momirealms.customcrops.api.core.world; + +import net.momirealms.customcrops.api.core.world.adaptor.WorldAdaptor; +import net.momirealms.customcrops.common.plugin.feature.Reloadable; +import org.bukkit.World; + +import java.util.Optional; +import java.util.Set; + +public interface WorldManager extends Reloadable { + + Season getSeason(World world); + + int getDate(World world); + + CustomCropsWorld loadWorld(World world); + + boolean unloadWorld(World world); + + Optional> getWorld(World world); + + Optional> getWorld(String world); + + boolean isWorldLoaded(World world); + + Set> adaptors(); + + CustomCropsWorld adapt(World world); + + CustomCropsWorld adapt(String world); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java similarity index 86% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java rename to api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java index e9a9b0df5..6df176de6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.world.level; +package net.momirealms.customcrops.api.core.world; public class WorldSetting implements Cloneable { @@ -36,7 +36,6 @@ public class WorldSetting implements Cloneable { private final boolean tickCropRandomly; private final boolean tickPotRandomly; private final boolean tickSprinklerRandomly; - private final boolean scheduledTick; private WorldSetting( boolean enableScheduler, @@ -74,7 +73,6 @@ private WorldSetting( this.tickCropRandomly = tickCropRandomly; this.tickPotRandomly = tickPotRandomly; this.tickSprinklerRandomly = tickSprinklerRandomly; - this.scheduledTick = !(tickCropRandomly && tickPotRandomly && tickSprinklerRandomly); } public static WorldSetting of( @@ -117,62 +115,58 @@ public static WorldSetting of( ); } - public boolean isSchedulerEnabled() { + public boolean enableScheduler() { return enableScheduler; } - public int getMinTickUnit() { + public int minTickUnit() { return minTickUnit; } - public int getTickCropInterval() { + public int tickCropInterval() { return tickCropInterval; } - public int getTickPotInterval() { + public int tickPotInterval() { return tickPotInterval; } - public int getTickSprinklerInterval() { + public int tickSprinklerInterval() { return tickSprinklerInterval; } - public boolean isOfflineTick() { + public boolean offlineTick() { return offlineTick; } - public boolean isEnableSeason() { + public boolean enableSeason() { return enableSeason; } - public boolean isAutoSeasonChange() { + public boolean autoSeasonChange() { return autoSeasonChange; } - public int getSeasonDuration() { + public int seasonDuration() { return seasonDuration; } - public int getPotPerChunk() { + public int potPerChunk() { return potPerChunk; } - public int getCropPerChunk() { + public int cropPerChunk() { return cropPerChunk; } - public int getSprinklerPerChunk() { + public int sprinklerPerChunk() { return sprinklerPerChunk; } - public int getRandomTickSpeed() { + public int randomTickSpeed() { return randomTickSpeed; } - public boolean isScheduledTick() { - return scheduledTick; - } - public boolean randomTickCrop() { return tickCropRandomly; } @@ -185,7 +179,7 @@ public boolean randomTickPot() { return tickPotRandomly; } - public int getMaxOfflineTime() { + public int maxOfflineTime() { return maxOfflineTime; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/AbstractWorldAdaptor.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/AbstractWorldAdaptor.java new file mode 100644 index 000000000..4934a6f10 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/AbstractWorldAdaptor.java @@ -0,0 +1,124 @@ +package net.momirealms.customcrops.api.core.world.adaptor; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.CompoundTag; +import com.flowpowered.nbt.IntArrayTag; +import com.flowpowered.nbt.StringTag; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.world.*; +import net.momirealms.customcrops.common.dependency.Dependency; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +public abstract class AbstractWorldAdaptor implements WorldAdaptor { + + public static final int CHUNK_VERSION = 2; + public static final int REGION_VERSION = 1; + + private final Method decompressMethod; + private final Method compressMethod; + + public AbstractWorldAdaptor() { + ClassLoader classLoader = BukkitCustomCropsPlugin.getInstance().getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.ZSTD)); + try { + Class zstd = classLoader.loadClass("com.github.luben.zstd.Zstd"); + decompressMethod = zstd.getMethod("decompress", byte[].class, byte[].class); + compressMethod = zstd.getMethod("compress", byte[].class); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + protected void zstdDecompress(byte[] decompressedData, byte[] compressedData) { + try { + decompressMethod.invoke(null, decompressedData, compressedData); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + //Zstd.decompress(decompressedData, compressedData); + } + + protected byte[] zstdCompress(byte[] data) { + try { + Object result = compressMethod.invoke(null, (Object) data); + return (byte[]) result; + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + //return Zstd.compress(data); + } + + @Override + public int compareTo(@NotNull WorldAdaptor o) { + return Integer.compare(o.priority(), this.priority()); + } + + protected SerializableChunk toSerializableChunk(CustomCropsChunk chunk) { + ChunkPos chunkPos = chunk.chunkPos(); + return new SerializableChunk( + chunkPos.x(), + chunkPos.z(), + chunk.loadedMilliSeconds(), + chunk.lastLoadedTime(), + chunk.sectionsToSave().map(this::toSerializableSection).toList(), + queueToIntArray(chunk.tickTaskQueue()), + tickedBlocksToArray(chunk.tickedBlocks()) + ); + } + + protected SerializableSection toSerializableSection(CustomCropsSection section) { + return new SerializableSection(section.getSectionID(), toCompoundTags(section.blockMap())); + } + + private List toCompoundTags(Map blocks) { + ArrayList tags = new ArrayList<>(blocks.size()); + Map> blockToPosMap = new HashMap<>(); + for (Map.Entry entry : blocks.entrySet()) { + BlockPos coordinate = entry.getKey(); + CustomCropsBlockState block = entry.getValue(); + List coordinates = blockToPosMap.computeIfAbsent(block, k -> new ArrayList<>()); + coordinates.add(coordinate.position()); + } + for (Map.Entry> entry : blockToPosMap.entrySet()) { + tags.add(new CompoundTag("", toCompoundMap(entry.getKey(), entry.getValue()))); + } + return tags; + } + + private CompoundMap toCompoundMap(CustomCropsBlockState block, List pos) { + CompoundMap map = new CompoundMap(); + int[] result = new int[pos.size()]; + for (int i = 0; i < pos.size(); i++) { + result[i] = pos.get(i); + } + map.put(new StringTag("type", block.type().type().asString())); + map.put(new IntArrayTag("pos", result)); + map.put(new CompoundTag("data", block.compoundMap().originalMap())); + return map; + } + + private int[] tickedBlocksToArray(Set set) { + int[] ticked = new int[set.size()]; + int i = 0; + for (BlockPos pos : set) { + ticked[i] = pos.position(); + i++; + } + return ticked; + } + + private int[] queueToIntArray(PriorityQueue queue) { + int size = queue.size() * 2; + int[] tasks = new int[size]; + int i = 0; + for (DelayedTickTask task : queue) { + tasks[i * 2] = task.getTime(); + tasks[i * 2 + 1] = task.blockPos().position(); + i++; + } + return tasks; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java new file mode 100644 index 000000000..8e578c54c --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java @@ -0,0 +1,45 @@ +package net.momirealms.customcrops.api.core.world.adaptor; + +import net.momirealms.customcrops.api.core.world.*; +import org.jetbrains.annotations.Nullable; + +public interface WorldAdaptor extends Comparable> { + + int BUKKIT_WORLD_PRIORITY = 100; + int SLIME_WORLD_PRIORITY = 200; + + WorldExtraData loadExtraData(W world); + + void saveExtraData(CustomCropsWorld world); + + /** + * Load the region from file or cache + */ + @Nullable + CustomCropsRegion loadRegion(CustomCropsWorld world, RegionPos pos, boolean createIfNotExist); + + /** + * Load the chunk from file or cache + */ + @Nullable + CustomCropsChunk loadChunk(CustomCropsWorld world, ChunkPos pos, boolean createIfNotExist); + + /** + * Unload the region to file or cache + */ + void saveRegion(CustomCropsWorld world, CustomCropsRegion region); + + void saveChunk(CustomCropsWorld world, CustomCropsChunk chunk); + + String getName(W world); + + @Nullable + W getWorld(String worldName); + + CustomCropsWorld adapt(Object world); + + long getWorldFullTime(W world); + + int priority(); + +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedBreakEvent.java new file mode 100644 index 000000000..88709352f --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedBreakEvent.java @@ -0,0 +1,103 @@ +package net.momirealms.customcrops.api.core.wrapper; + +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WrappedBreakEvent { + + private final Entity entity; + private final Block block; + private final Location location; + private final String brokenID; + private final ItemStack itemInHand; + private final String itemID; + private final Cancellable event; + private final CustomCropsWorld world; + private final BreakReason reason; + + public WrappedBreakEvent( + @Nullable Entity entityBreaker, + @Nullable Block blockBreaker, + CustomCropsWorld world, + Location location, + String brokenID, + ItemStack itemInHand, + String itemID, + BreakReason reason, + Cancellable event + ) { + this.entity = entityBreaker; + this.block = blockBreaker; + this.location = location; + this.brokenID = brokenID; + this.itemInHand = itemInHand; + this.itemID = itemID; + this.event = event; + this.world = world; + this.reason = reason; + } + + @NotNull + public BreakReason reason() { + return reason; + } + + @NotNull + public CustomCropsWorld world() { + return world; + } + + @NotNull + public Location location() { + return location; + } + + @NotNull + public String brokenID() { + return brokenID; + } + + @Nullable + public ItemStack itemInHand() { + return itemInHand; + } + + @Nullable + public String itemID() { + return itemID; + } + + public boolean isCancelled() { + return event.isCancelled(); + } + + public void setCancelled(boolean cancel) { + event.setCancelled(cancel); + } + + @Nullable + public Player playerBreaker() { + if (entity instanceof Player player) { + return player; + } + return null; + } + + @Nullable + public Entity entityBreaker() { + return entity; + } + + @Nullable + public Block blockBreaker() { + return block; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractAirEvent.java b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractAirEvent.java new file mode 100644 index 000000000..c827c2f55 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractAirEvent.java @@ -0,0 +1,43 @@ +package net.momirealms.customcrops.api.core.wrapper; + +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +public class WrappedInteractAirEvent { + + private final CustomCropsWorld world; + private final ItemStack itemInHand; + private final String itemID; + private final EquipmentSlot hand; + private final Player player; + + public WrappedInteractAirEvent(CustomCropsWorld world, Player player, EquipmentSlot hand, ItemStack itemInHand, String itemID) { + this.world = world; + this.itemInHand = itemInHand; + this.itemID = itemID; + this.hand = hand; + this.player = player; + } + + public CustomCropsWorld world() { + return world; + } + + public ItemStack itemInHand() { + return itemInHand; + } + + public String itemID() { + return itemID; + } + + public EquipmentSlot hand() { + return hand; + } + + public Player player() { + return player; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractEvent.java new file mode 100644 index 000000000..c114d3d32 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractEvent.java @@ -0,0 +1,94 @@ +package net.momirealms.customcrops.api.core.wrapper; + +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import org.bukkit.Location; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +public class WrappedInteractEvent { + + private final CustomCropsWorld world; + private final String relatedID; + private final ItemStack itemInHand; + private final String itemID; + private final EquipmentSlot hand; + private final BlockFace blockFace; + private final Cancellable event; + private final Location clickedLocation; + private final ExistenceForm existenceForm; + private final Player player; + + public WrappedInteractEvent( + ExistenceForm existenceForm, + Player player, + CustomCropsWorld world, + Location clickedLocation, + String relatedID, + ItemStack itemInHand, + String itemID, + EquipmentSlot hand, + BlockFace blockFace, + Cancellable event + ) { + this.player = player; + this.world = world; + this.clickedLocation = clickedLocation; + this.relatedID = relatedID; + this.itemID = itemID; + this.itemInHand = itemInHand; + this.hand = hand; + this.blockFace = blockFace; + this.event = event; + this.existenceForm = existenceForm; + } + + public CustomCropsWorld world() { + return world; + } + + public boolean isCancelled() { + return event.isCancelled(); + } + + public void setCancelled(boolean cancel) { + event.setCancelled(cancel); + } + + public ExistenceForm existenceForm() { + return existenceForm; + } + + public Location location() { + return clickedLocation; + } + + public String relatedID() { + return relatedID; + } + + public ItemStack itemInHand() { + return itemInHand; + } + + public String itemID() { + return itemID; + } + + public EquipmentSlot hand() { + return hand; + } + + @Nullable + public BlockFace clickedBlockFace() { + return blockFace; + } + + public Player player() { + return player; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedPlaceEvent.java new file mode 100644 index 000000000..612e799c8 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedPlaceEvent.java @@ -0,0 +1,67 @@ +package net.momirealms.customcrops.api.core.wrapper; + +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +public class WrappedPlaceEvent { + + private final Player player; + private final Location location; + private final String placedID; + private final EquipmentSlot hand; + private final ItemStack item; + private final String itemID; + private final Cancellable event; + private final CustomCropsWorld world; + + public WrappedPlaceEvent(Player player, CustomCropsWorld world, Location location, String placedID, EquipmentSlot hand, ItemStack item, String itemID, Cancellable event) { + this.player = player; + this.location = location; + this.hand = hand; + this.item = item; + this.itemID = itemID; + this.world = world; + this.event = event; + this.placedID = placedID; + } + + public boolean isCancelled() { + return event.isCancelled(); + } + + public void setCancelled(boolean cancel) { + event.setCancelled(cancel); + } + + public String placedID() { + return placedID; + } + + public CustomCropsWorld world() { + return world; + } + + public Player player() { + return player; + } + + public Location location() { + return location; + } + + public EquipmentSlot hand() { + return hand; + } + + public ItemStack item() { + return item; + } + + public String itemID() { + return itemID; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java index 6216a37fc..17a8c8e48 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java @@ -17,41 +17,48 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.BoneMeal; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.core.block.BoneMeal; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; /** * An event that triggered when a player interacts a crop with a bone meal */ -public class BoneMealUseEvent extends Event implements Cancellable { +public class BoneMealUseEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; private final BoneMeal boneMeal; - private final WorldCrop crop; + private final CustomCropsBlockState blockState; private final ItemStack itemInHand; - private final Player player; + private final EquipmentSlot equipmentSlot; + private final CropConfig config; public BoneMealUseEvent( @NotNull Player player, @NotNull ItemStack itemInHand, @NotNull Location location, @NotNull BoneMeal boneMeal, - @NotNull WorldCrop crop + @NotNull CustomCropsBlockState blockState, + @NotNull EquipmentSlot equipmentSlot, + @NotNull CropConfig config ) { + super(player); this.location = location; - this.crop = crop; + this.blockState = blockState; this.boneMeal = boneMeal; this.itemInHand = itemInHand; - this.player = player; + this.equipmentSlot = equipmentSlot; + this.config = config; } @Override @@ -85,6 +92,11 @@ public Location getLocation() { return location; } + @NotNull + public CropConfig getConfig() { + return config; + } + /** * Get the item in player's hand * If there's nothing in hand, it would return AIR @@ -96,14 +108,14 @@ public ItemStack getItemInHand() { return itemInHand; } - /** - * Get the crop's data - * - * @return crop data - */ @NotNull - public WorldCrop getCrop() { - return crop; + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public EquipmentSlot getEquipmentSlot() { + return equipmentSlot; } /** @@ -115,9 +127,4 @@ public WorldCrop getCrop() { public BoneMeal getBoneMeal() { return boneMeal; } - - @NotNull - public Player getPlayer() { - return player; - } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java index 9a2bb76cb..03b09d2b7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java @@ -17,11 +17,12 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.misc.Reason; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; +import org.bukkit.block.Block; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -29,44 +30,86 @@ import org.jetbrains.annotations.Nullable; /** - * An event that triggered when breaking a crop + * The CropBreakEvent class represents an event that occurs when a crop block is broken + * in the CustomCrops plugin. This event is triggered under various circumstances such + * as a player breaking the crop, trampling, explosions, or specific actions. */ public class CropBreakEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; + private final Entity entityBreaker; + private final Block blockBreaker; private final Location location; - private final WorldCrop worldCrop; - private final Entity entity; - private final Reason reason; + private final CropConfig config; + private final String stageItemID; + private final CustomCropsBlockState blockState; + private final BreakReason reason; + /** + * Constructor for the CropBreakEvent. + * + * @param entityBreaker The entity that caused the crop to break (can be null). + * @param blockBreaker The block that caused the crop to break (can be null). + * @param config The crop configuration associated with the crop. + * @param stageItemID The item ID representing the stage of the crop. + * @param location The location of the crop block that is being broken. + * @param blockState The data of the crop block before it was broken. + * @param reason The reason for the crop break. + */ public CropBreakEvent( - @Nullable Entity entity, + @Nullable Entity entityBreaker, + @Nullable Block blockBreaker, + @NotNull CropConfig config, + @NotNull String stageItemID, @NotNull Location location, - @NotNull WorldCrop worldCrop, - @NotNull Reason reason + @NotNull CustomCropsBlockState blockState, + @NotNull BreakReason reason ) { - this.entity = entity; this.location = location; - this.worldCrop = worldCrop; + this.blockState = blockState; this.reason = reason; + this.entityBreaker = entityBreaker; + this.blockBreaker = blockBreaker; + this.config = config; + this.stageItemID = stageItemID; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -74,53 +117,72 @@ public HandlerList getHandlers() { } /** - * Get the crop's data, it might be null if it's spawned by other plugins in the wild + * Gets the location of the crop block that is being broken. * - * @return crop data + * @return the location of the broken crop block. */ - @Nullable - public WorldCrop getWorldCrop() { - return worldCrop; + @NotNull + public Location getLocation() { + return location; } /** - * Get the crop location + * Gets the block responsible for breaking the crop, if applicable. * - * @return location + * @return the block that caused the break, or null if not applicable. */ - @NotNull - public Location getLocation() { - return location; + @Nullable + public Block getBlockBreaker() { + return blockBreaker; } /** - * Get the entity that triggered the event - * This would be null if the crop is broken by respawn anchor - * The breaker can be a TNT, creeper. - * If the pot is a vanilla farmland, it can be trampled by entities + * Gets the entity responsible for breaking the crop, if applicable. * - * @return entity + * @return the entity that caused the break, or null if not applicable. */ @Nullable - public Entity getEntity() { - return entity; + public Entity getEntityBreaker() { + return entityBreaker; } + /** + * Gets the state of the crop block before it was broken. + * + * @return the block state before breakage, or null if not applicable. + */ @Nullable - public Player getPlayer() { - if (entity instanceof Player player) { - return player; - } - return null; + public CustomCropsBlockState getBlockState() { + return blockState; + } + + /** + * Gets the item ID representing the stage of the crop. + * + * @return the stage item ID. + */ + @NotNull + public String getStageItemID() { + return stageItemID; + } + + /** + * Gets the crop configuration associated with the broken block. + * + * @return the crop configuration. + */ + @NotNull + public CropConfig getCropConfig() { + return config; } /** - * Get the reason + * Gets the reason for the crop block breakage. * - * @return reason + * @return the reason for the break. */ @NotNull - public Reason getReason() { + public BreakReason getReason() { return reason; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java index 8e55b851c..8f89ee3df 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java @@ -17,15 +17,16 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * An event that triggered when a player interacts a crop @@ -35,19 +36,28 @@ public class CropInteractEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final WorldCrop crop; + private final CustomCropsBlockState blockState; private final ItemStack itemInHand; + private final EquipmentSlot hand; + private final CropConfig config; + private final String stageItemID; public CropInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @NotNull Location location, - @Nullable WorldCrop crop + @NotNull CustomCropsBlockState blockState, + @NotNull EquipmentSlot hand, + @NotNull CropConfig config, + @NotNull String stageItemID ) { super(who); this.location = location; - this.crop = crop; + this.blockState = blockState; this.itemInHand = itemInHand; + this.hand = hand; + this.config = config; + this.stageItemID = stageItemID; } @Override @@ -92,13 +102,23 @@ public ItemStack getItemInHand() { return itemInHand; } - /** - * Get the crop's data, it might be null if it's spawned by other plugins in the wild - * - * @return crop data - */ - @Nullable - public WorldCrop getCrop() { - return crop; + @NotNull + public CropConfig getCropConfig() { + return config; + } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; + } + + @NotNull + public String getStageItemID() { + return stageItemID; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java index 3b04f9dcd..21d15d6f9 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java @@ -17,12 +17,14 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -34,22 +36,28 @@ public class CropPlantEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final ItemStack itemInHand; - private final Crop crop; + private final CropConfig config; private final Location location; + private final CustomCropsBlockState blockState; + private final EquipmentSlot hand; private int point; public CropPlantEvent( @NotNull Player who, @NotNull ItemStack itemInHand, + @NotNull EquipmentSlot hand, @NotNull Location location, - @NotNull Crop crop, + @NotNull CropConfig config, + @NotNull CustomCropsBlockState blockState, int point ) { super(who); this.itemInHand = itemInHand; + this.hand = hand; this.location = location; - this.crop = crop; + this.config = config; this.point = point; + this.blockState = blockState; } @Override @@ -83,14 +91,24 @@ public HandlerList getHandlers() { return getHandlerList(); } + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; + } + /** * Get the crop's config * * @return crop */ @NotNull - public Crop getCrop() { - return crop; + public CropConfig getCropConfig() { + return config; } /** diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java index 4d939cf43..8aa810b68 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; @@ -25,9 +25,9 @@ public class CustomCropsReloadEvent extends Event { private static final HandlerList handlerList = new HandlerList(); - private final CustomCropsPlugin plugin; + private final BukkitCustomCropsPlugin plugin; - public CustomCropsReloadEvent(CustomCropsPlugin plugin) { + public CustomCropsReloadEvent(BukkitCustomCropsPlugin plugin) { this.plugin = plugin; } @@ -41,7 +41,7 @@ public HandlerList getHandlers() { return getHandlerList(); } - public CustomCropsPlugin getPluginInstance() { + public BukkitCustomCropsPlugin getPluginInstance() { return plugin; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java index 4376d9c09..85a276cd3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java @@ -17,13 +17,15 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.item.Fertilizer; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -36,22 +38,28 @@ public class FertilizerUseEvent extends PlayerEvent implements Cancellable { private boolean cancelled; private final ItemStack itemInHand; private final Location location; - private final WorldPot pot; + private final CustomCropsBlockState blockState; private final Fertilizer fertilizer; + private final EquipmentSlot hand; + private final PotConfig config; public FertilizerUseEvent( @NotNull Player player, @NotNull ItemStack itemInHand, @NotNull Fertilizer fertilizer, @NotNull Location location, - @NotNull WorldPot pot + @NotNull CustomCropsBlockState blockState, + @NotNull EquipmentSlot hand, + @NotNull PotConfig config ) { super(player); this.cancelled = false; this.itemInHand = itemInHand; this.fertilizer = fertilizer; this.location = location; - this.pot = pot; + this.blockState = blockState; + this.hand = hand; + this.config = config; } @Override @@ -94,14 +102,19 @@ public Location getLocation() { return location; } - /** - * Get the pot's data - * - * @return pot data - */ @NotNull - public WorldPot getPot() { - return pot; + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; + } + + @NotNull + public PotConfig getPotConfig() { + return config; } /** diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java index 9a5c8bfc3..b89871692 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java @@ -17,11 +17,11 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.misc.Reason; -import net.momirealms.customcrops.api.mechanic.world.level.WorldGlass; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; +import org.bukkit.block.Block; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -36,20 +36,26 @@ public class GreenhouseGlassBreakEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final Entity entity; - private final Reason reason; - private final WorldGlass glass; + private final Entity entityBreaker; + private final Block blockBreaker; + private final BreakReason reason; + private final CustomCropsBlockState blockState; + private final String glassItemID; public GreenhouseGlassBreakEvent( - @Nullable Entity entity, + @Nullable Entity entityBreaker, + @Nullable Block blockBreaker, @NotNull Location location, - @NotNull WorldGlass glass, - @NotNull Reason reason + @NotNull String glassItemID, + @NotNull CustomCropsBlockState blockState, + @NotNull BreakReason reason ) { - this.entity = entity; + this.entityBreaker = entityBreaker; + this.blockBreaker = blockBreaker; this.location = location; this.reason = reason; - this.glass = glass; + this.blockState = blockState; + this.glassItemID = glassItemID; } @Override @@ -84,30 +90,26 @@ public Location getLocation() { } @Nullable - public Entity getEntity() { - return entity; + public Entity getEntityBreaker() { + return entityBreaker; } @Nullable - public Player getPlayer() { - if (entity instanceof Player player) { - return player; - } - return null; + public Block getBlockBreaker() { + return blockBreaker; } @NotNull - public Reason getReason() { + public BreakReason getReason() { return reason; } - /** - * Get the glass data - * - * @return glass data - */ @NotNull - public WorldGlass getGlass() { - return glass; + public CustomCropsBlockState getBlockState() { + return blockState; + } + + public String getGlassItemID() { + return glassItemID; } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java similarity index 57% rename from api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java rename to api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java index ed4ed5428..df0a46bcf 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java @@ -17,41 +17,43 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.BoneMeal; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; -import org.bukkit.block.Block; +import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; /** - * An event that triggered when the bone meal is dispensed + * An event that triggered when interacting a sprinkler */ -public class BoneMealDispenseEvent extends Event implements Cancellable { +public class GreenhouseGlassInteractEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final BoneMeal boneMeal; - private final WorldCrop crop; - private final ItemStack boneMealItem; - private final Block dispenser; + private final CustomCropsBlockState blockState; + private final ItemStack itemInHand; + private final String glassItemID; + private final EquipmentSlot hand; - public BoneMealDispenseEvent( - @NotNull Block dispenser, - @NotNull ItemStack boneMealItem, + public GreenhouseGlassInteractEvent( + @NotNull Player who, + @NotNull ItemStack itemInHand, @NotNull Location location, - @NotNull BoneMeal boneMeal, - @NotNull WorldCrop crop + @NotNull String glassItemID, + @NotNull CustomCropsBlockState blockState, + @NotNull EquipmentSlot hand ) { + super(who); this.location = location; - this.crop = crop; - this.boneMeal = boneMeal; - this.boneMealItem = boneMealItem; - this.dispenser = dispenser; + this.itemInHand = itemInHand; + this.hand = hand; + this.blockState = blockState; + this.glassItemID = glassItemID; } @Override @@ -76,7 +78,8 @@ public HandlerList getHandlers() { } /** - * Get the crop location + * Get the sprinkler location + * * @return location */ @NotNull @@ -84,43 +87,28 @@ public Location getLocation() { return location; } - /** - * Get the item in player's hand - * If there's nothing in hand, it would return AIR - * @return item in hand - */ @NotNull - public ItemStack getBoneMealItem() { - return boneMealItem; + public CustomCropsBlockState getBlockState() { + return blockState; } - /** - * Get the crop's data - * - * @return crop data - */ @NotNull - public WorldCrop getCrop() { - return crop; + public EquipmentSlot getHand() { + return hand; } - /** - * Get the bone meal's config - * - * @return bone meal config - */ @NotNull - public BoneMeal getBoneMeal() { - return boneMeal; + public String getGlassItemID() { + return glassItemID; } /** - * Get the dispenser block + * Get the item in player's hand * - * @return dispenser block + * @return item in hand */ @NotNull - public Block getDispenser() { - return dispenser; + public ItemStack getItemInHand() { + return itemInHand; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java index f16133505..a5cfc215c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -32,13 +33,19 @@ public class GreenhouseGlassPlaceEvent extends PlayerEvent implements Cancellabl private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; + private final CustomCropsBlockState blockState; + private final String glassItemID; public GreenhouseGlassPlaceEvent( @NotNull Player who, - @NotNull Location location + @NotNull Location location, + @NotNull String glassItemID, + @NotNull CustomCropsBlockState blockState ) { super(who); this.location = location; + this.glassItemID = glassItemID; + this.blockState = blockState; } @Override @@ -71,4 +78,14 @@ public HandlerList getHandlers() { public Location getLocation() { return location; } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public String getGlassItemID() { + return glassItemID; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java index a5cbb08fa..d443f4ecd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java @@ -17,9 +17,11 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.misc.Reason; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; +import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -36,20 +38,26 @@ public class PotBreakEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final WorldPot pot; - private final Entity entity; - private final Reason reason; + private final PotConfig config; + private final CustomCropsBlockState blockState; + private final Entity entityBreaker; + private final Block blockBreaker; + private final BreakReason reason; public PotBreakEvent( - @Nullable Entity entity, + @Nullable Entity entityBreaker, + @Nullable Block blockBreaker, @NotNull Location location, - @NotNull WorldPot pot, - @NotNull Reason reason + @NotNull PotConfig config, + @NotNull CustomCropsBlockState blockState, + @NotNull BreakReason reason ) { - this.entity = entity; + this.entityBreaker = entityBreaker; + this.blockBreaker = blockBreaker; this.location = location; - this.pot = pot; + this.blockState = blockState; this.reason = reason; + this.config = config; } @Override @@ -83,32 +91,36 @@ public Location getLocation() { return location; } - - /** - * Get the pot's data - * - * @return pot - */ - @NotNull - public WorldPot getPot() { - return pot; - } - @Nullable - public Entity getEntity() { - return entity; + public Entity getEntityBreaker() { + return entityBreaker; } @Nullable public Player getPlayer() { - if (entity instanceof Player player) { + if (entityBreaker instanceof Player player) { return player; } return null; } @NotNull - public Reason getReason() { + public BreakReason getReason() { return reason; } + + @NotNull + public PotConfig getPotConfig() { + return config; + } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @Nullable + public Block getBlockBreaker() { + return blockBreaker; + } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java index 7a947e751..2b7323353 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java @@ -17,13 +17,15 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -35,22 +37,28 @@ public class PotFillEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final WorldPot pot; + private final PotConfig config; private final ItemStack itemInHand; - private final PassiveFillMethod fillMethod; + private final WateringMethod wateringMethod; + private final CustomCropsBlockState blockState; + private final EquipmentSlot hand; public PotFillEvent( @NotNull Player player, @NotNull ItemStack itemInHand, + @NotNull EquipmentSlot hand, @NotNull Location location, - @NotNull PassiveFillMethod fillMethod, - @NotNull WorldPot pot + @NotNull WateringMethod wateringMethod, + @NotNull CustomCropsBlockState blockState, + @NotNull PotConfig config ) { super(player); this.location = location; this.itemInHand = itemInHand; - this.pot = pot; - this.fillMethod = fillMethod; + this.wateringMethod = wateringMethod; + this.blockState = blockState; + this.config = config; + this.hand = hand; } @Override @@ -84,17 +92,6 @@ public Location getLocation() { return location; } - - /** - * Get the pot's data - * - * @return pot - */ - @NotNull - public WorldPot getPot() { - return pot; - } - /** * Get the item in hand * @@ -105,13 +102,23 @@ public ItemStack getItemInHand() { return itemInHand; } - /** - * Get the passive fill method - * - * @return passive fill method - */ @NotNull - public PassiveFillMethod getFillMethod() { - return fillMethod; + public PotConfig getPotConfig() { + return config; + } + + @NotNull + public WateringMethod getWateringMethod() { + return wateringMethod; + } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java index f1f5c6578..fc59d6c44 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java @@ -17,12 +17,14 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -35,18 +37,24 @@ public class PotInteractEvent extends PlayerEvent implements Cancellable { private boolean cancelled; private final ItemStack itemInHand; private final Location location; - private final WorldPot pot; + private final CustomCropsBlockState blockState; + private final PotConfig config; + private final EquipmentSlot hand; public PotInteractEvent( @NotNull Player who, + @NotNull EquipmentSlot hand, @NotNull ItemStack itemInHand, + @NotNull PotConfig config, @NotNull Location location, - @NotNull WorldPot pot + @NotNull CustomCropsBlockState blockState ) { super(who); this.itemInHand = itemInHand; this.location = location; - this.pot = pot; + this.blockState = blockState; + this.config = config; + this.hand = hand; } @Override @@ -81,6 +89,11 @@ public ItemStack getItemInHand() { return itemInHand; } + @NotNull + public EquipmentSlot getHand() { + return hand; + } + /** * Get the pot location * @@ -97,7 +110,12 @@ public Location getLocation() { * @return pot key */ @NotNull - public WorldPot getPot() { - return pot; + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public PotConfig getPotConfig() { + return config; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java index 4f8570f30..23e2f94ee 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java @@ -17,12 +17,15 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; /** @@ -33,16 +36,25 @@ public class PotPlaceEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final Pot pot; + private final PotConfig config; + private final CustomCropsBlockState state; + private final ItemStack itemInHand; + private final EquipmentSlot hand; public PotPlaceEvent( @NotNull Player who, @NotNull Location location, - @NotNull Pot pot + @NotNull PotConfig config, + @NotNull CustomCropsBlockState state, + @NotNull ItemStack itemInHand, + @NotNull EquipmentSlot hand ) { super(who); this.location = location; - this.pot = pot; + this.state = state; + this.itemInHand = itemInHand; + this.hand = hand; + this.config = config; } @Override @@ -76,13 +88,23 @@ public Location getLocation() { return location; } - /** - * Get the placed pot's config - * - * @return pot - */ @NotNull - public Pot getPot() { - return pot; + public CustomCropsBlockState getState() { + return state; + } + + @NotNull + public ItemStack getItemInHand() { + return itemInHand; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; + } + + @NotNull + public PotConfig getPotConfig() { + return config; } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java index d9a2a00ac..65b8ac192 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java @@ -17,11 +17,11 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.misc.Reason; -import net.momirealms.customcrops.api.mechanic.world.level.WorldScarecrow; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; +import org.bukkit.block.Block; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -36,20 +36,26 @@ public class ScarecrowBreakEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final Entity entity; - private final Reason reason; - private final WorldScarecrow scarecrow; + private final Entity entityBreaker; + private final Block blockBreaker; + private final BreakReason reason; + private final CustomCropsBlockState blockState; + private final String scarecrowItemID; public ScarecrowBreakEvent( - @Nullable Entity entity, + @Nullable Entity entityBreaker, + @Nullable Block blockBreaker, @NotNull Location location, - @NotNull WorldScarecrow scarecrow, - @NotNull Reason reason + @NotNull String scarecrowItemID, + @NotNull CustomCropsBlockState blockState, + @NotNull BreakReason reason ) { this.location = location; + this.entityBreaker = entityBreaker; + this.blockBreaker = blockBreaker; this.reason = reason; - this.entity = entity; - this.scarecrow = scarecrow; + this.blockState = blockState; + this.scarecrowItemID = scarecrowItemID; } @Override @@ -84,25 +90,26 @@ public Location getLocation() { } @Nullable - public Entity getEntity() { - return entity; + public Entity getEntityBreaker() { + return entityBreaker; } @Nullable - public Player getPlayer() { - if (entity instanceof Player player) { - return player; - } - return null; + public Block getBlockBreaker() { + return blockBreaker; } @NotNull - public Reason getReason() { + public BreakReason getReason() { return reason; } @NotNull - public WorldScarecrow getScarecrow() { - return scarecrow; + public CustomCropsBlockState getBlockState() { + return blockState; + } + + public String getScarecrowItemID() { + return scarecrowItemID; } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java new file mode 100644 index 000000000..c84cddec0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) <2022> + * + * 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.event; + +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +/** + * An event that triggered when interacting a sprinkler + */ +public class ScarecrowInteractEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Location location; + private final CustomCropsBlockState blockState; + private final ItemStack itemInHand; + private final String scarecrowItemID; + private final EquipmentSlot hand; + + public ScarecrowInteractEvent( + @NotNull Player who, + @NotNull ItemStack itemInHand, + @NotNull Location location, + @NotNull String scarecrowItemID, + @NotNull CustomCropsBlockState blockState, + @NotNull EquipmentSlot hand + ) { + super(who); + this.location = location; + this.itemInHand = itemInHand; + this.hand = hand; + this.blockState = blockState; + this.scarecrowItemID = scarecrowItemID; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return getHandlerList(); + } + + /** + * Get the sprinkler location + * + * @return location + */ + @NotNull + public Location getLocation() { + return location; + } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; + } + + @NotNull + public String getScarecrowItemID() { + return scarecrowItemID; + } + + /** + * Get the item in player's hand + * + * @return item in hand + */ + @NotNull + public ItemStack getItemInHand() { + return itemInHand; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java index 981aa8b37..c55657d04 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -32,13 +33,19 @@ public class ScarecrowPlaceEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; + private final String scarecrowItemID; + private final CustomCropsBlockState blockState; public ScarecrowPlaceEvent( @NotNull Player who, - @NotNull Location location + @NotNull Location location, + @NotNull String scarecrowItemID, + @NotNull CustomCropsBlockState blockState ) { super(who); this.location = location; + this.blockState = blockState; + this.scarecrowItemID = scarecrowItemID; } @Override @@ -62,6 +69,11 @@ public HandlerList getHandlers() { return getHandlerList(); } + @NotNull + public String getScarecrowItemID() { + return scarecrowItemID; + } + /** * Get the scarecrow location * @@ -71,4 +83,9 @@ public HandlerList getHandlers() { public Location getLocation() { return location; } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java deleted file mode 100644 index 6a67aa17e..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.event; - -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import org.bukkit.World; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -/** - * An async event triggered when season changes - */ -public class SeasonChangeEvent extends Event { - - private static final HandlerList handlers = new HandlerList(); - private final Season season; - private final World world; - - public SeasonChangeEvent( - @NotNull World world, - @NotNull Season season - ) { - super(true); - this.world = world; - this.season = season; - } - - @NotNull - public static HandlerList getHandlerList() { - return handlers; - } - - @NotNull - @Override - public HandlerList getHandlers() { - return getHandlerList(); - } - - /** - * Get the new season - * - * @return season - */ - @NotNull - public Season getSeason() { - return season; - } - - /** - * Get the world - * - * @return world - */ - public World getWorld() { - return world; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java index 34c797c94..dfa508d8b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java @@ -17,11 +17,12 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.misc.Reason; -import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; +import org.bukkit.block.Block; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -36,20 +37,26 @@ public class SprinklerBreakEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final WorldSprinkler sprinkler; - private final Entity entity; - private final Reason reason; + private final CustomCropsBlockState blockState; + private final SprinklerConfig config; + private final Entity entityBreaker; + private final Block blockBreaker; + private final BreakReason reason; public SprinklerBreakEvent( - @Nullable Entity entity, + @Nullable Entity entityBreaker, + @Nullable Block blockBreaker, @NotNull Location location, - @NotNull WorldSprinkler sprinkler, - @NotNull Reason reason + @NotNull CustomCropsBlockState blockState, + @NotNull SprinklerConfig config, + @NotNull BreakReason reason ) { - this.entity = entity; + this.entityBreaker = entityBreaker; + this.blockBreaker = blockBreaker; this.location = location; this.reason = reason; - this.sprinkler = sprinkler; + this.config = config; + this.blockState = blockState; } @Override @@ -83,31 +90,28 @@ public Location getLocation() { return location; } - /** - * Get the sprinkler's data - * - * @return sprinkler - */ + @Nullable + public Entity getEntityBreaker() { + return entityBreaker; + } + @NotNull - public WorldSprinkler getSprinkler() { - return sprinkler; + public CustomCropsBlockState getBlockState() { + return blockState; } - @Nullable - public Entity getEntity() { - return entity; + @NotNull + public SprinklerConfig getSprinklerConfig() { + return config; } @Nullable - public Player getPlayer() { - if (entity instanceof Player player) { - return player; - } - return null; + public Block getBlockBreaker() { + return blockBreaker; } @NotNull - public Reason getReason() { + public BreakReason getReason() { return reason; } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java index 32a26a28a..ae59f85e2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java @@ -17,40 +17,48 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; -import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; /** - * An event that triggered when a sprinkler is watered by the fill-methods set in each sprinkler's config + * An event that triggered when a pot is watered by the fill-methods set in each sprinkler's config */ public class SprinklerFillEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; - private final ItemStack itemInHand; private final Location location; - private final PassiveFillMethod fillMethod; - private final WorldSprinkler sprinkler; + private final SprinklerConfig config; + private final ItemStack itemInHand; + private final WateringMethod wateringMethod; + private final CustomCropsBlockState blockState; + private final EquipmentSlot hand; public SprinklerFillEvent( @NotNull Player player, @NotNull ItemStack itemInHand, + @NotNull EquipmentSlot hand, @NotNull Location location, - @NotNull PassiveFillMethod fillMethod, - @NotNull WorldSprinkler sprinkler + @NotNull WateringMethod wateringMethod, + @NotNull CustomCropsBlockState blockState, + @NotNull SprinklerConfig config ) { super(player); - this.itemInHand = itemInHand; this.location = location; - this.fillMethod = fillMethod; - this.sprinkler = sprinkler; + this.itemInHand = itemInHand; + this.wateringMethod = wateringMethod; + this.blockState = blockState; + this.config = config; + this.hand = hand; } @Override @@ -60,7 +68,7 @@ public boolean isCancelled() { @Override public void setCancelled(boolean cancel) { - this.cancelled = cancel; + cancelled = cancel; } @NotNull @@ -75,17 +83,7 @@ public HandlerList getHandlers() { } /** - * Get the item in player's hand - * - * @return item in hand - */ - @NotNull - public ItemStack getItemInHand() { - return itemInHand; - } - - /** - * Get the sprinkler location + * Get the pot location * * @return location */ @@ -95,17 +93,32 @@ public Location getLocation() { } /** - * Get the passive fill method + * Get the item in hand * - * @return passive fill method + * @return item in hand */ @NotNull - public PassiveFillMethod getFillMethod() { - return fillMethod; + public ItemStack getItemInHand() { + return itemInHand; + } + + @NotNull + public SprinklerConfig getSprinklerConfig() { + return config; + } + + @NotNull + public WateringMethod getWateringMethod() { + return wateringMethod; + } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; } @NotNull - public WorldSprinkler getSprinkler() { - return sprinkler; + public EquipmentSlot getHand() { + return hand; } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java index d671fe3bf..7aaf4b2c2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java @@ -17,12 +17,14 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -34,19 +36,25 @@ public class SprinklerInteractEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final WorldSprinkler sprinkler; + private final CustomCropsBlockState blockState; + private final SprinklerConfig config; private final ItemStack itemInHand; + private final EquipmentSlot hand; public SprinklerInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @NotNull Location location, - @NotNull WorldSprinkler sprinkler + @NotNull SprinklerConfig config, + @NotNull CustomCropsBlockState blockState, + @NotNull EquipmentSlot hand ) { super(who); this.location = location; - this.sprinkler = sprinkler; + this.config = config; this.itemInHand = itemInHand; + this.hand = hand; + this.blockState = blockState; } @Override @@ -80,14 +88,19 @@ public Location getLocation() { return location; } - /** - * Get the sprinkler's data - * - * @return sprinkler - */ @NotNull - public WorldSprinkler getSprinkler() { - return sprinkler; + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public SprinklerConfig getSprinklerConfig() { + return config; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; } /** diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java index d1469d8d2..e2b149408 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java @@ -17,12 +17,14 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -35,18 +37,24 @@ public class SprinklerPlaceEvent extends PlayerEvent implements Cancellable { private boolean cancelled; private final ItemStack itemInHand; private final Location location; - private final Sprinkler sprinkler; + private final SprinklerConfig config; + private final CustomCropsBlockState blockState; + private final EquipmentSlot hand; public SprinklerPlaceEvent( @NotNull Player who, @NotNull ItemStack itemInHand, + @NotNull EquipmentSlot hand, @NotNull Location location, - @NotNull Sprinkler sprinkler + @NotNull SprinklerConfig config, + CustomCropsBlockState blockState ) { super(who); this.itemInHand = itemInHand; this.location = location; - this.sprinkler = sprinkler; + this.config = config; + this.hand = hand; + this.blockState = blockState; } @Override @@ -80,6 +88,11 @@ public ItemStack getItemInHand() { return itemInHand; } + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + /** * Get the sprinkler location * @@ -90,13 +103,13 @@ public Location getLocation() { return location; } - /** - * Get the sprinkler's config - * - * @return sprinkler - */ @NotNull - public Sprinkler getSprinkler() { - return sprinkler; + public SprinklerConfig getSprinklerConfig() { + return config; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java index 9c4e3a353..05b930a7e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java @@ -17,13 +17,14 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.WateringCan; -import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; +import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.water.FillMethod; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -35,23 +36,26 @@ public class WateringCanFillEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final ItemStack itemInHand; - private final WateringCan wateringCan; - private final PositiveFillMethod fillMethod; + private final WateringCanConfig config; + private final FillMethod fillMethod; private final Location location; + private final EquipmentSlot hand; public WateringCanFillEvent( @NotNull Player player, + @NotNull EquipmentSlot hand, @NotNull ItemStack itemInHand, @NotNull Location location, - @NotNull WateringCan wateringCan, - @NotNull PositiveFillMethod fillMethod + @NotNull WateringCanConfig config, + @NotNull FillMethod fillMethod ) { super(player); this.cancelled = false; this.itemInHand = itemInHand; - this.wateringCan = wateringCan; + this.config = config; this.location = location; this.fillMethod = fillMethod; + this.hand = hand; } @Override @@ -84,14 +88,9 @@ public ItemStack getItemInHand() { return itemInHand; } - /** - * Get watering can config - * - * @return watering can config - */ @NotNull - public WateringCan getWateringCan() { - return wateringCan; + public WateringCanConfig getConfig() { + return config; } /** @@ -100,10 +99,15 @@ public WateringCan getWateringCan() { * @return positive fill method */ @NotNull - public PositiveFillMethod getFillMethod() { + public FillMethod getFillMethod() { return fillMethod; } + @NotNull + public EquipmentSlot getHand() { + return hand; + } + /** * Get the location * diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java similarity index 62% rename from api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterEvent.java rename to api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java index 20efeadd0..e1e45f2db 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java @@ -17,43 +17,48 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.mechanic.item.WateringCan; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import org.bukkit.Location; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.world.Pos3; +import net.momirealms.customcrops.common.util.Pair; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import java.util.Set; +import java.util.List; /** * An event that triggered when player tries to use watering-can to add water to pots/sprinklers */ -public class WateringCanWaterEvent extends PlayerEvent implements Cancellable { +public class WateringCanWaterPotEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final ItemStack itemInHand; - private final WateringCan wateringCan; - private final CustomCropsBlock potOrSprinkler; - private final Set location; + private final EquipmentSlot hand; + private final WateringCanConfig wateringCanConfig; + private final PotConfig potConfig; + private final List> potWithIDs; - public WateringCanWaterEvent( + public WateringCanWaterPotEvent( @NotNull Player player, @NotNull ItemStack itemInHand, - @NotNull Set location, - @NotNull WateringCan wateringCan, - @NotNull CustomCropsBlock potOrSprinkler + @NotNull EquipmentSlot hand, + @NotNull WateringCanConfig wateringCanConfig, + @NotNull PotConfig potConfig, + List> potWithIDs ) { super(player); this.cancelled = false; this.itemInHand = itemInHand; - this.wateringCan = wateringCan; - this.location = location; - this.potOrSprinkler = potOrSprinkler; + this.hand = hand; + this.wateringCanConfig = wateringCanConfig; + this.potConfig = potConfig; + this.potWithIDs = potWithIDs; } @Override @@ -86,33 +91,23 @@ public ItemStack getItemInHand() { return itemInHand; } - /** - * Get the watering can's config - * - * @return watering can config - */ @NotNull - public WateringCan getWateringCan() { - return wateringCan; + public EquipmentSlot getHand() { + return hand; } - /** - * Get the locations that involved in this event - * - * @return locations - */ @NotNull - public Set getLocation() { - return location; + public WateringCanConfig getWateringCanConfig() { + return wateringCanConfig; + } + + @NotNull + public PotConfig getPotConfig() { + return potConfig; } - /** - * Get the pot/sprinkler's data - * - * @return data - */ @NotNull - public CustomCropsBlock getPotOrSprinkler() { - return potOrSprinkler; + public List> getPotWithIDs() { + return potWithIDs; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java new file mode 100644 index 000000000..5d31bbf9e --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) <2022> + * + * 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.event; + +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +/** + * An event that triggered when player tries to use watering-can to add water to pots/sprinklers + */ +public class WateringCanWaterSprinklerEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final ItemStack itemInHand; + private final EquipmentSlot hand; + private final WateringCanConfig wateringCanConfig; + private final SprinklerConfig sprinklerConfig; + private final CustomCropsBlockState blockState; + private final Location location; + + public WateringCanWaterSprinklerEvent( + @NotNull Player player, + @NotNull ItemStack itemInHand, + @NotNull EquipmentSlot hand, + @NotNull WateringCanConfig wateringCanConfig, + @NotNull SprinklerConfig sprinklerConfig, + @NotNull CustomCropsBlockState blockState, + @NotNull Location location + ) { + super(player); + this.cancelled = false; + this.itemInHand = itemInHand; + this.hand = hand; + this.wateringCanConfig = wateringCanConfig; + this.sprinklerConfig = sprinklerConfig; + this.blockState = blockState; + this.location = location; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public @NotNull HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * Get the watering can item + * + * @return watering can item + */ + @NotNull + public ItemStack getItemInHand() { + return itemInHand; + } + + @NotNull + public EquipmentSlot getHand() { + return hand; + } + + @NotNull + public WateringCanConfig getWateringCanConfig() { + return wateringCanConfig; + } + + @NotNull + public SprinklerConfig getSprinklerConfig() { + return sprinklerConfig; + } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + + @NotNull + public Location getLocation() { + return location; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java b/api/src/main/java/net/momirealms/customcrops/api/integration/ExternalProvider.java similarity index 64% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java rename to api/src/main/java/net/momirealms/customcrops/api/integration/ExternalProvider.java index 47389e484..67a868844 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/ExternalProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,16 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.action; +package net.momirealms.customcrops.api.integration; -public interface ActionFactory { +/** + * The ExternalProvider interface serves as a base interface for various external providers + */ +public interface ExternalProvider { /** - * Build an action by args and chance + * Gets the identification of the external provider. * - * @param args args - * @param chance chance (0-1) - * @return action + * @return The identification string of the external provider. */ - Action build(Object args, double chance); + String identifier(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/IntegrationManager.java b/api/src/main/java/net/momirealms/customcrops/api/integration/IntegrationManager.java new file mode 100644 index 000000000..66058147e --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/IntegrationManager.java @@ -0,0 +1,78 @@ +/* + * 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.integration; + +import net.momirealms.customcrops.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Interface for managing integration providers. + * This allows for the registration and retrieval of various types of providers + * such as Leveler, Enchantment, and Season providers. + */ +public interface IntegrationManager extends Reloadable { + + /** + * Registers a LevelerProvider. + * + * @param levelerProvider the LevelerProvider to register + * @return true if registration is successful, false otherwise + */ + boolean registerLevelerProvider(@NotNull LevelerProvider levelerProvider); + + /** + * Unregisters a LevelerProvider by its ID. + * + * @param id the ID of the LevelerProvider to unregister + * @return true if unregistration is successful, false otherwise + */ + boolean unregisterLevelerProvider(@NotNull String id); + + /** + * Registers a SeasonProvider. + * + * @param seasonProvider the SeasonProvider to register + */ + void registerSeasonProvider(@NotNull SeasonProvider seasonProvider); + + /** + * Retrieves a registered LevelerProvider by its ID. + * + * @param id the ID of the LevelerProvider to retrieve + * @return the LevelerProvider if found, or null if not found + */ + @Nullable + LevelerProvider getLevelerProvider(String id); + + /** + * Registers an ItemProvider. + * + * @param itemProvider the ItemProvider to register + * @return true if registration is successful, false otherwise. + */ + boolean registerItemProvider(@NotNull ItemProvider itemProvider); + + /** + * Unregisters an ItemProvider by its ID. + * + * @param id the ID of the ItemProvider to unregister + * @return true if unregistration is successful, false otherwise. + */ + boolean unregisterItemProvider(@NotNull String id); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java b/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java deleted file mode 100644 index 66a49b0b3..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.integration; - -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -public interface ItemLibrary { - - /** - * Get the identification - * for instance "CustomItems" - * - * @return identification - */ - String identification(); - - /** - * Build an item instance for a player - * - * @param player player - * @param id id - * @return item - */ - ItemStack buildItem(Player player, String id); - - /** - * Get an item's id - * - * @param itemStack item - * @return ID - */ - String getItemID(ItemStack itemStack); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/ItemProvider.java b/api/src/main/java/net/momirealms/customcrops/api/integration/ItemProvider.java new file mode 100644 index 000000000..910e5a849 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/ItemProvider.java @@ -0,0 +1,49 @@ +/* + * 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.integration; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Interface representing a provider for custom items. + * This interface allows for building items for players and retrieving item IDs from item stacks. + */ +public interface ItemProvider extends ExternalProvider { + + /** + * Builds an ItemStack for a player based on a specified item ID. + * + * @param player the player for whom the item is being built. + * @param id the ID of the item to build. + * @return the built ItemStack. + */ + @NotNull + ItemStack buildItem(@NotNull Player player, @NotNull String id); + + /** + * Retrieves the item ID from a given ItemStack. + * + * @param itemStack the ItemStack from which to retrieve the item ID. + * @return the item ID as a string, or null if the item stack does not have an associated ID. + */ + @Nullable + String itemID(@NotNull ItemStack itemStack); +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/LevelInterface.java b/api/src/main/java/net/momirealms/customcrops/api/integration/LevelerProvider.java similarity index 64% rename from api/src/main/java/net/momirealms/customcrops/api/integration/LevelInterface.java rename to api/src/main/java/net/momirealms/customcrops/api/integration/LevelerProvider.java index b34761365..b9d1e9029 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/integration/LevelInterface.java +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/LevelerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -18,8 +18,15 @@ package net.momirealms.customcrops.api.integration; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public interface LevelInterface { +/** + * The LevelerProvider interface defines methods to interact with external leveling + * systems, allowing the management of experience points (XP) and levels for various + * skills or jobs. Implementations of this interface should provide the logic for + * adding XP to players and retrieving their levels in specific skills or jobs. + */ +public interface LevelerProvider extends ExternalProvider { /** * Add exp to a certain skill or job @@ -28,7 +35,7 @@ public interface LevelInterface { * @param target the skill or job, for instance "Fishing" "fisherman" * @param amount the exp amount */ - void addXp(Player player, String target, double amount); + void addXp(@NotNull Player player, @NotNull String target, double amount); /** * Get a player's skill or job's level @@ -37,5 +44,5 @@ public interface LevelInterface { * @param target the skill or job, for instance "Fishing" "fisherman" * @return level */ - int getLevel(Player player, String target); + int getLevel(@NotNull Player player, @NotNull String target); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/SeasonInterface.java b/api/src/main/java/net/momirealms/customcrops/api/integration/SeasonProvider.java similarity index 53% rename from api/src/main/java/net/momirealms/customcrops/api/integration/SeasonInterface.java rename to api/src/main/java/net/momirealms/customcrops/api/integration/SeasonProvider.java index 95229020d..a698a47b4 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/integration/SeasonInterface.java +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/SeasonProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -17,41 +17,24 @@ package net.momirealms.customcrops.api.integration; -import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.api.core.world.Season; import org.bukkit.World; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; -public interface SeasonInterface { +/** + * The SeasonProvider interface defines methods to interact with external seasonal + * systems, allowing the retrieval of the current season for a specific world. + * Implementations of this interface should provide the logic for determining the + * season based on the world context. + */ +public interface SeasonProvider extends ExternalProvider { /** * Get a world's season * * @param world world - * @return spring, summer, autumn, winter or null - */ - @Nullable Season getSeason(World world); - - /** - * Get a world's date - * - * @param world world - * @return date - */ - int getDate(World world); - - /** - * Set a world's season - * - * @param world world - * @param season season - */ - void setSeason(World world, Season season); - - /** - * Set a world's date - * - * @param world world - * @param date date + * @return spring, summer, autumn, winter or disabled */ - void setDate(World world, int date); + @NotNull + Season getSeason(@NotNull World world); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java deleted file mode 100644 index 47d4b34bc..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.common.Reloadable; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionFactory; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import org.bukkit.configuration.ConfigurationSection; - -import java.util.HashMap; - -public interface ActionManager extends Reloadable { - - /** - * Register a custom action type - * - * @param type type - * @param actionFactory action factory - * @return success or not - */ - boolean registerAction(String type, ActionFactory actionFactory); - - /** - * Unregister an action type by id - * - * @param type type - * @return success or not - */ - boolean unregisterAction(String type); - - /** - * Build an action instance with Bukkit configs - * - * @param section bukkit config - * @return action - */ - Action getAction(ConfigurationSection section); - - /** - * If an action type exists - * - * @param type type - * @return exist or not - */ - default boolean hasAction(String type) { - return getActionFactory(type) != null; - } - - /** - * Build an action map with Bukkit configs - * - * @param section bukkit config - * @return action map - */ - HashMap getActionMap(ConfigurationSection section); - - /** - * Build actions with Bukkit configs - * - * @param section bukkit config - * @return actions - */ - Action[] getActions(ConfigurationSection section); - - /** - * Get an action factory by type - * - * @param type type - * @return action factory - */ - ActionFactory getActionFactory(String type); - - /** - * Trigger actions - * - * @param state state - * @param actions actions - */ - static void triggerActions(State state, Action... actions) { - if (actions != null) - for (Action action : actions) - action.trigger(state); - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/AdventureManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/AdventureManager.java deleted file mode 100644 index fce5c77d8..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/AdventureManager.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.text.Component; -import net.momirealms.customcrops.api.common.Initable; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -public abstract class AdventureManager implements Initable { - - private static AdventureManager instance; - - public AdventureManager() { - instance = this; - } - - public static AdventureManager getInstance() { - return instance; - } - - public abstract void sendMessage(CommandSender sender, String s); - - public abstract void sendMessageWithPrefix(CommandSender sender, String text); - - public abstract void sendConsoleMessage(String text); - - public abstract void sendPlayerMessage(Player player, String text); - - public abstract void sendActionbar(Player player, String text); - - public abstract void sendSound(Player player, Sound.Source source, Key key, float pitch, float volume); - - public abstract void sendSound(Player player, Sound sound); - - public abstract Component getComponentFromMiniMessage(String text); - - public abstract String legacyToMiniMessage(String legacy); - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public abstract boolean isColorCode(char c); - - public abstract void sendTitle(Player player, String title, String subTitle, int fadeIn, int stay, int fadeOut); - - public abstract int rgbaToDecimal(String rgba); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java deleted file mode 100644 index 91ce5a161..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.common.Reloadable; -import net.momirealms.customcrops.api.mechanic.condition.Condition; -import net.momirealms.customcrops.api.mechanic.condition.ConditionFactory; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import org.bukkit.configuration.ConfigurationSection; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface ConditionManager extends Reloadable { - - /** - * Register a custom condition type - * - * @param type type - * @param conditionFactory condition factory - * @return success or not - */ - boolean registerCondition(String type, ConditionFactory conditionFactory); - - /** - * Unregister a condition type by id - * - * @param type type - * @return success or not - */ - boolean unregisterCondition(String type); - - /** - * If a condition type exists - * - * @param type type - * @return exist or not - */ - default boolean hasCondition(String type) { - return getConditionFactory(type) != null; - } - - /** - * Build conditions with Bukkit configs - * - * @param section bukkit config - * @return conditions - */ - @NotNull - Condition[] getConditions(ConfigurationSection section); - - /** - * Build a condition instance with Bukkit configs - * - * @param section bukkit config - * @return condition - */ - Condition getCondition(ConfigurationSection section); - - /** - * Build a condition instance with Bukkit configs - * - * @return condition - */ - Condition getCondition(String key, Object args); - - /** - * Get a condition factory by type - * - * @param type type - * @return condition factory - */ - @Nullable ConditionFactory getConditionFactory(String type); - - /** - * Are conditions met for a custom crops block - * - * @param block block - * @param offline is the check for offline ticks - * @param conditions conditions - * @return meet or not - */ - static boolean isConditionMet(CustomCropsBlock block, boolean offline, Condition... conditions) { - if (conditions == null) return true; - for (Condition condition : conditions) { - if (!condition.isConditionMet(block, offline)) { - return false; - } - } - return true; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java deleted file mode 100644 index 7820679de..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.common.Reloadable; -import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; -import org.bukkit.World; - -public abstract class ConfigManager implements Reloadable { - - private static ConfigManager instance; - - public ConfigManager() { - instance = this; - } - - public static ConfigManager getInstance() { - return instance; - } - - public static boolean legacyColorSupport() { - return instance.hasLegacyColorSupport(); - } - - public static int maximumPoolSize() { - return instance.getMaximumPoolSize(); - } - - public static int corePoolSize() { - return instance.getCorePoolSize(); - } - - public static int keepAliveTime() { - return instance.getKeepAliveTime(); - } - - public static boolean debug() { - return instance.getDebugMode(); - } - - public static boolean protectLore() { - return instance.isProtectLore(); - } - - public static String[] itemDetectionOrder() { - return instance.getItemDetectionOrder(); - } - - public static String lang() { - return instance.getLang(); - } - - public static boolean metrics() { - return instance.hasMetrics(); - } - - public static boolean checkUpdate() { - return instance.hasCheckUpdate(); - } - - public static double[] defaultQualityRatio() { - return instance.getDefaultQualityRatio(); - } - - public static boolean preventTrampling() { - return instance.isPreventTrampling(); - } - - public static boolean disableMoisture() { - return instance.isDisableMoisture(); - } - - public static boolean syncSeasons() { - return instance.isSyncSeasons(); - } - - public static boolean enableGreenhouse() { - return instance.isGreenhouseEnabled(); - } - - public static World referenceWorld() { - return instance.getReferenceWorld(); - } - - public static int greenhouseRange() { - return instance.getGreenhouseRange(); - } - - public static int scarecrowRange() { - return instance.getScarecrowRange(); - } - - public static String greenhouseID() { - return instance.getGreenhouseID(); - } - - public static boolean enableScarecrow() { - return instance.isScarecrowEnabled(); - } - - public static String scarecrowID() { - return instance.getScarecrowID(); - } - - public static boolean convertWorldOnLoad() { - return instance.isConvertWorldOnLoad(); - } - - public static boolean scarecrowProtectChunk() { - return instance.doesScarecrowProtectChunk(); - } - - public static ItemCarrier scarecrowItemCarrier() { - return instance.getScarecrowItemCarrier(); - } - - public static ItemCarrier glassItemCarrier() { - return instance.getGlassItemCarrier(); - } - - public abstract boolean isConvertWorldOnLoad(); - - public abstract double[] getDefaultQualityRatio(); - - public abstract String getLang(); - - public abstract boolean getDebugMode(); - - public abstract boolean hasLegacyColorSupport(); - - public abstract int getMaximumPoolSize(); - - public abstract int getKeepAliveTime(); - - public abstract int getCorePoolSize(); - - public abstract boolean isProtectLore(); - - public abstract String[] getItemDetectionOrder(); - - public abstract boolean hasMetrics(); - - public abstract boolean hasCheckUpdate(); - - public abstract boolean isDisableMoisture(); - - public abstract boolean isPreventTrampling(); - - public abstract boolean isGreenhouseEnabled(); - - public abstract String getGreenhouseID(); - - public abstract int getGreenhouseRange(); - - public abstract boolean isScarecrowEnabled(); - - public abstract String getScarecrowID(); - - public abstract int getScarecrowRange(); - - public abstract boolean isSyncSeasons(); - - public abstract boolean doesScarecrowProtectChunk(); - - public abstract ItemCarrier getScarecrowItemCarrier(); - - public abstract ItemCarrier getGlassItemCarrier(); - - public abstract World getReferenceWorld(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java deleted file mode 100644 index aa2e90fc1..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.common.Initable; -import net.momirealms.customcrops.api.integration.LevelInterface; -import net.momirealms.customcrops.api.integration.SeasonInterface; -import org.jetbrains.annotations.Nullable; - -public interface IntegrationManager extends Initable { - - /** - * Registers a level plugin with the specified name. - * - * @param plugin The name of the level plugin. - * @param level The implementation of the LevelInterface. - * @return true if the registration was successful, false if the plugin name is already registered. - */ - boolean registerLevelPlugin(String plugin, LevelInterface level); - - /** - * Unregisters a level plugin with the specified name. - * - * @param plugin The name of the level plugin to unregister. - * @return true if the unregistration was successful, false if the plugin name is not found. - */ - boolean unregisterLevelPlugin(String plugin); - - /** - * Get the LevelInterface provided by a plugin. - * - * @param plugin The name of the plugin providing the LevelInterface. - * @return The LevelInterface provided by the specified plugin, or null if the plugin is not registered. - */ - @Nullable LevelInterface getLevelPlugin(String plugin); - - /** - * Get the SeasonInterface provided by a plugin. - * - * @return the season interface - */ - SeasonInterface getSeasonInterface(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java deleted file mode 100644 index 4056afe8e..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.common.Reloadable; -import net.momirealms.customcrops.api.integration.ItemLibrary; -import net.momirealms.customcrops.api.mechanic.item.*; -import net.momirealms.customcrops.api.mechanic.misc.CRotation; -import org.bukkit.Location; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.List; - -public interface ItemManager extends Reloadable { - - /** - * Register an item library - * - * @param itemLibrary item library - * @return success or not - */ - boolean registerItemLibrary(@NotNull ItemLibrary itemLibrary); - - /** - * Unregister an item library by identification - * - * @param identification identification - * @return success or not - */ - boolean unregisterItemLibrary(String identification); - - /** - * Get item's id by ItemStack - * ItemsAdder: namespace:id - * Oraxen: id - * Vanilla: CAPITAL_ID - * Other item libraries: LibraryID:ItemID - * - * @param itemStack item - * @return ID - */ - String getItemID(ItemStack itemStack); - - /** - * Get item by ID - * ItemsAdder: namespace:id - * Oraxen: id - * Vanilla: CAPITAL_ID - * Other item libraries: LibraryID:ItemID - * - * @param player player - * @param id id - * @return item - */ - ItemStack getItemStack(Player player, String id); - - /** - * Place an item at a certain location - * - * @param location location - * @param carrier carrier - * @param id id - */ - void placeItem(Location location, ItemCarrier carrier, String id); - - /** - * Place an item at a certain location - * - * @param location location - * @param carrier carrier - * @param id id - * @param rotation rotation - */ - void placeItem(Location location, ItemCarrier carrier, String id, CRotation rotation); - - /** - * Remove any block/entity from a certain location - * - * @param location location - * @return the rotation of the removed entity - */ - CRotation removeAnythingAt(Location location); - - /** - * Get the rotation of the removed entity - * - * @param location location - * @return rotation - */ - CRotation getRotation(Location location); - - /** - * Get watering can config by ID - * - * @param id id - * @return watering can config - */ - @Nullable - WateringCan getWateringCanByID(@NotNull String id); - - /** - * Get watering can config by item ID - * - * @param id item ID - * @return watering can config - */ - @Nullable - WateringCan getWateringCanByItemID(@NotNull String id); - - /** - * Get watering can config by itemStack - * - * @param itemStack itemStack - * @return watering can config - */ - @Nullable - WateringCan getWateringCanByItemStack(@NotNull ItemStack itemStack); - - /** - * Get sprinkler config by ID - * - * @param id id - * @return sprinkler config - */ - @Nullable - Sprinkler getSprinklerByID(@NotNull String id); - - /** - * Get sprinkler config by 3D item ID - * - * @param id 3D item ID - * @return sprinkler config - */ - @Nullable - Sprinkler getSprinklerBy3DItemID(@NotNull String id); - - /** - * Get sprinkler config by 2D item ID - * - * @param id 2D item ID - * @return sprinkler config - */ - @Nullable - Sprinkler getSprinklerBy2DItemID(@NotNull String id); - - /** - * Get sprinkler config by entity - * - * @param entity entity - * @return sprinkler config - */ - @Nullable - Sprinkler getSprinklerByEntity(@NotNull Entity entity); - - /** - * Get sprinkler config by block - * - * @param block block - * @return sprinkler config - */ - @Nullable - Sprinkler getSprinklerByBlock(@NotNull Block block); - - /** - * Get sprinkler config by 2D itemStack - * - * @param itemStack 2D itemStack - * @return sprinkler config - */ - @Nullable - Sprinkler getSprinklerBy2DItemStack(@NotNull ItemStack itemStack); - - /** - * Get sprinkler config by 3D itemStack - * - * @param itemStack 3D itemStack - * @return sprinkler config - */ - @Nullable - Sprinkler getSprinklerBy3DItemStack(@NotNull ItemStack itemStack); - - /** - * Get pot config by ID - * - * @param id id - * @return pot config - */ - @Nullable - Pot getPotByID(@NotNull String id); - - /** - * Get pot config by block ID - * - * @param id block ID - * @return pot config - */ - @Nullable - Pot getPotByBlockID(@NotNull String id); - - /** - * Get pot config by block - * - * @param block block - * @return pot config - */ - @Nullable - Pot getPotByBlock(@NotNull Block block); - - /** - * Get pot config by block itemStack - * - * @param itemStack itemStack - * @return pot config - */ - @Nullable - Pot getPotByItemStack(@NotNull ItemStack itemStack); - - /** - * Get fertilizer config by ID - * - * @param id id - * @return fertilizer config - */ - @Nullable - Fertilizer getFertilizerByID(String id); - - /** - * Get fertilizer config by item ID - * - * @param id item id - * @return fertilizer config - */ - @Nullable - Fertilizer getFertilizerByItemID(String id); - - /** - * Get fertilizer config by itemStack - * - * @param itemStack itemStack - * @return fertilizer config - */ - @Nullable - Fertilizer getFertilizerByItemStack(@NotNull ItemStack itemStack); - - /** - * Get crop config by ID - * - * @param id id - * @return crop config - */ - @Nullable - Crop getCropByID(String id); - - /** - * Get crop config by seed ID - * - * @param id seed ID - * @return crop config - */ - @Nullable - Crop getCropBySeedID(String id); - - /** - * Get crop config by seed itemStack - * - * @param itemStack seed itemStack - * @return crop config - */ - @Nullable - Crop getCropBySeedItemStack(ItemStack itemStack); - - /** - * Get crop config by stage item ID - * - * @param id stage item ID - * @return crop config - */ - @Nullable - Crop getCropByStageID(String id); - - /** - * Get crop config by entity - * - * @param entity entity - * @return crop config - */ - @Nullable - Crop getCropByEntity(Entity entity); - - /** - * Get crop config by block - * - * @param block block - * @return crop config - */ - @Nullable - Crop getCropByBlock(Block block); - - /** - * Get crop stage config by stage ID - * - * @param id stage ID - * @return crop stage config - */ - @Nullable - Crop.Stage getCropStageByStageID(String id); - - /** - * Update a pot's block state - * - * @param location location - * @param pot pot config - * @param hasWater has water or not - * @param fertilizer fertilizer - */ - void updatePotState(Location location, Pot pot, boolean hasWater, Fertilizer fertilizer); - - /** - * Get the pots that can be watered with a watering can - * - * @param baseLocation the clicked pot's location - * @param width width of the working range - * @param length length of the working range - * @param yaw player's yaw - * @param potID pot's ID - * @return the pots that can be watered - */ - @NotNull - Collection getPotInRange(Location baseLocation, int width, int length, float yaw, String potID); - - void handlePlayerInteractBlock( - Player player, - Block clickedBlock, - BlockFace clickedFace, - Cancellable event - ); - - void handlePlayerInteractAir( - Player player, - Cancellable event - ); - - void handlePlayerBreakBlock( - Player player, - Block brokenBlock, - String blockID, - Cancellable event - ); - - void handlePlayerInteractFurniture( - Player player, - Location location, - String id, - Entity baseEntity, - Cancellable event - ); - - void handlePlayerPlaceFurniture( - Player player, - Location location, - String id, - Cancellable event - ); - - void handlePlayerBreakFurniture( - Player player, - Location location, - String id, - Cancellable event - ); - - void handlePlayerPlaceBlock( - Player player, - Block block, - String blockID, - Cancellable event - ); - - void handleEntityTramplingBlock( - Entity entity, - Block block, - Cancellable event - ); - - void handleExplosion( - Entity entity, - List blocks, - Cancellable event - ); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/MessageManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/MessageManager.java deleted file mode 100644 index 7fc653bad..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/MessageManager.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import org.jetbrains.annotations.Nullable; - -public abstract class MessageManager { - - private static MessageManager instance; - - public MessageManager() { - instance = this; - } - - public static MessageManager getInstance() { - return instance; - } - - public static String seasonTranslation(@Nullable Season season) { - return instance.getSeasonTranslation(season); - } - - public static String reloadMessage() { - return instance.getReload(); - } - - public static String prefix() { - return instance.getPrefix(); - } - - protected abstract String getPrefix(); - - protected abstract String getReload(); - - protected abstract String getSeasonTranslation(Season season); - - public abstract void reload(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/PlaceholderManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/PlaceholderManager.java deleted file mode 100644 index a720b1862..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/PlaceholderManager.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.common.Reloadable; -import org.bukkit.entity.Player; - -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -public abstract class PlaceholderManager implements Reloadable { - - public static final Pattern pattern = Pattern.compile("\\{[^{}]+}"); - private static PlaceholderManager instance; - - public PlaceholderManager() { - instance = this; - } - - public static PlaceholderManager getInstance() { - return instance; - } - - public abstract String parse(Player player, String text, Map vars); - - public abstract List parse(Player player, List text, Map vars); - - public abstract List detectPlaceholders(String text); - - public abstract String setPlaceholders(Player player, String text); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java deleted file mode 100644 index b4f6efb48..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.common.Reloadable; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.requirement.RequirementFactory; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import org.bukkit.configuration.ConfigurationSection; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface RequirementManager extends Reloadable { - - /** - * Register a custom requirement type - * - * @param type type - * @param requirementFactory requirement factory - * @return success or not - */ - boolean registerRequirement(String type, RequirementFactory requirementFactory); - - /** - * Unregister a custom requirement by type - * - * @param type type - * @return success or not - */ - boolean unregisterRequirement(String type); - - /** - * Build requirements with Bukkit configs - * - * @param section bukkit config - * @param advanced check "not-met-actions" or not - * @return requirements - */ - @Nullable - Requirement[] getRequirements(ConfigurationSection section, boolean advanced); - - /** - * If a requirement type exists - * - * @param type type - * @return exist or not - */ - default boolean hasRequirement(String type) { - return getRequirementFactory(type) != null; - } - - /** - * Build a requirement instance with Bukkit configs - * - * @param section bukkit config - * @param advanced check "not-met-actions" or not - * @return requirement - */ - @NotNull - Requirement getRequirement(ConfigurationSection section, boolean advanced); - - /** - * Build a requirement instance with Bukkit configs - * - * @return requirement - */ - @NotNull - Requirement getRequirement(String type, Object value); - - /** - * Get a requirement factory by type - * - * @param type type - * @return requirement factory - */ - @Nullable - RequirementFactory getRequirementFactory(String type); - - /** - * Are requirements met for a player - * - * @param state state - * @param requirements requirements - * @return meet or not - */ - static boolean isRequirementMet(State state, Requirement... requirements) { - if (requirements == null) return true; - for (Requirement requirement : requirements) { - if (!requirement.isStateMet(state)) { - return false; - } - } - return true; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java deleted file mode 100644 index 3e874a66f..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import java.util.concurrent.CompletionStage; - -public abstract class VersionManager { - - private static VersionManager instance; - - public VersionManager() { - instance = this; - } - - public static VersionManager getInstance() { - return instance; - } - - public static boolean isHigherThan1_19_R3() { - return instance.isVersionNewerThan1_19_R3(); - } - - public static boolean isHigherThan1_19_R2() { - return instance.isVersionNewerThan1_19_R2(); - } - - public static boolean isHigherThan1_18() { - return instance.isVersionNewerThan1_18(); - } - - public static boolean isHigherThan1_19() { - return instance.isVersionNewerThan1_19(); - } - - public static boolean isHigherThan1_20() { - return instance.isVersionNewerThan1_20(); - } - - public static boolean isHigherThan1_20_R2() { - return instance.isVersionNewerThan1_20_R2(); - } - - public abstract boolean isVersionNewerThan1_20_R2(); - - public abstract boolean hasRegionScheduler(); - - public static boolean folia() { - return instance.hasRegionScheduler(); - } - - public abstract String getPluginVersion(); - - public static String pluginVersion() { - return instance.getPluginVersion(); - } - - public static boolean spigot() { - return instance.isSpigot(); - } - - public abstract boolean isSpigot(); - - public abstract boolean isVersionNewerThan1_19_R3(); - - public abstract boolean isVersionNewerThan1_19(); - - public abstract boolean isVersionNewerThan1_19_R2(); - - public abstract boolean isVersionNewerThan1_20(); - - public abstract boolean isVersionNewerThan1_18(); - - public abstract boolean isMojmap(); - - public abstract CompletionStage checkUpdate(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java deleted file mode 100644 index edff0c586..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.common.Reloadable; -import net.momirealms.customcrops.api.mechanic.item.*; -import net.momirealms.customcrops.api.mechanic.world.AbstractWorldAdaptor; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.*; -import org.bukkit.Chunk; -import org.bukkit.World; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.Optional; - -public interface WorldManager extends Reloadable { - - /** - * Load a specified world and convert it into a CustomCrops world - * This method ignores the whitelist and blacklist - * If there already exists one, it would not create a new instance but return the created one - * - * @param world world - */ - @NotNull - CustomCropsWorld loadWorld(@NotNull World world); - - /** - * Unload a specified world and save it to file - * This method ignores the whitelist and blacklist - * - * @param world world - */ - boolean unloadWorld(@NotNull World world); - - /** - * Check if the world has CustomCrops mechanisms - * - * @param world world - * @return has or not - */ - boolean isMechanicEnabled(@NotNull World world); - - /** - * Get all the worlds loaded in CustomCrops - * - * @return worlds - */ - @NotNull - Collection getWorldNames(); - - /** - * Get all the worlds loaded in CustomCrops - * - * @return worlds - */ - @NotNull - Collection getBukkitWorlds(); - - /** - * Get all the worlds loaded in CustomCrops - * - * @return worlds - */ - @NotNull - Collection getCustomCropsWorlds(); - - /** - * Get CustomCrops world by name - * - * @param name name - * @return CustomCrops world - */ - @NotNull - Optional getCustomCropsWorld(@NotNull String name); - - /** - * Get CustomCrops world by Bukkit world - * - * @param world world - * @return CustomCrops world - */ - @NotNull - Optional getCustomCropsWorld(@NotNull World world); - - /** - * Get sprinkler at a certain location - * - * @param location location - * @return sprinkler - */ - @NotNull - Optional getSprinklerAt(@NotNull SimpleLocation location); - - /** - * Get pot at a certain location - * - * @param location location - * @return pot - */ - @NotNull - Optional getPotAt(@NotNull SimpleLocation location); - - /** - * Get crop at a certain location - * - * @param location location - * @return crop - */ - @NotNull - Optional getCropAt(@NotNull SimpleLocation location); - - /** - * Get greenhouse glass at a certain location - * - * @param location location - * @return greenhouse glass - */ - @NotNull Optional getGlassAt(@NotNull SimpleLocation location); - - /** - * Get scarecrow at a certain location - * - * @param location location - * @return scarecrow - */ - @NotNull Optional getScarecrowAt(@NotNull SimpleLocation location); - - /** - * Get any CustomCrops block at a certain location - * The block can be crop, sprinkler and etc. - * - * @param location location - * @return CustomCrops block - */ - Optional getBlockAt(SimpleLocation location); - - /** - * Create crop data - * - * @param location location - * @param crop crop config - * @param point initial point - * @return the crop data - */ - WorldCrop createCropData(SimpleLocation location, Crop crop, int point); - - /** - * Create crop data - * - * @param location location - * @param crop crop config - * @return the crop data - */ - default WorldCrop createCropData(SimpleLocation location, Crop crop) { - return createCropData(location, crop, 0); - } - - /** - * Create sprinkler data - * - * @param location location - * @param sprinkler sprinkler config - * @param water initial water - * @return the sprinkler data - */ - WorldSprinkler createSprinklerData(SimpleLocation location, Sprinkler sprinkler, int water); - - /** - * Create sprinkler data - * - * @param location location - * @param sprinkler sprinkler config - * @return the sprinkler data - */ - default WorldSprinkler createSprinklerData(SimpleLocation location, Sprinkler sprinkler) { - return createSprinklerData(location, sprinkler, 0); - } - - /** - * Create pot data - * - * @param location location - * @param pot pot config - * @param water initial water - * @param fertilizer fertilizer config - * @param fertilizerTimes the remaining usages of the fertilizer - * @return the pot data - */ - WorldPot createPotData(SimpleLocation location, Pot pot, int water, @Nullable Fertilizer fertilizer, int fertilizerTimes); - - /** - * Create pot data - * - * @param location location - * @param pot pot config - * @return the pot data - */ - default WorldPot createPotData(SimpleLocation location, Pot pot) { - return createPotData(location, pot, 0, null, 0); - } - - /** - * Create Greenhouse glass data - * - * @param location location - * @return the greenhouse glass data - */ - WorldGlass createGreenhouseGlassData(SimpleLocation location); - - /** - * Create scarecrow data - * - * @param location location - * @return the scarecrow data - */ - WorldScarecrow createScarecrowData(SimpleLocation location); - - /** - * Add water to the sprinkler - * This method would create new sprinkler data if the sprinkler data not exists in that place - * This method would also update the sprinkler's model if it has models according to the water amount - * - * @param sprinkler sprinkler config - * @param location location - * @param amount amount of water - */ - void addWaterToSprinkler(@NotNull Sprinkler sprinkler, @NotNull SimpleLocation location, int amount); - - /** - * Add fertilizer to the pot - * This method would create new pot data if the pot data not exists in that place - * This method would update the pot's block state if it has appearance variations for different fertilizers - * - * @param pot pot config - * @param fertilizer fertilizer config - * @param location location - */ - void addFertilizerToPot(@NotNull Pot pot, @NotNull Fertilizer fertilizer, @NotNull SimpleLocation location); - - /** - * Add water to the pot - * This method would create new pot data if the pot data not exists in that place - * This method would update the pot's block state if it's dry - * - * @param pot pot config - * @param amount amount of water - * @param location location - */ - void addWaterToPot(@NotNull Pot pot, int amount, @NotNull SimpleLocation location); - - /** - * Add points to a crop - * This method would do nothing if the crop data not exists in that place - * This method would change the stage of the crop and trigger the actions - * - * @param crop crop config - * @param points points to add - * @param location location - */ - void addPointToCrop(@NotNull Crop crop, int points, @NotNull SimpleLocation location); - - /** - * Add greenhouse glass data - * - * @param glass glass data - * @param location location - */ - void addGlassAt(@NotNull WorldGlass glass, @NotNull SimpleLocation location); - - /** - * Add pot data - * - * @param pot pot data - * @param location location - */ - void addPotAt(@NotNull WorldPot pot, @NotNull SimpleLocation location); - - /** - * Add sprinkler data - * - * @param sprinkler sprinkler data - * @param location location - */ - void addSprinklerAt(@NotNull WorldSprinkler sprinkler, @NotNull SimpleLocation location); - - /** - * Add crop data - * - * @param crop crop data - * @param location location - */ - void addCropAt(@NotNull WorldCrop crop, @NotNull SimpleLocation location); - - /** - * Add scarecrow data - * - * @param scarecrow scarecrow data - * @param location location - */ - void addScarecrowAt(@NotNull WorldScarecrow scarecrow, @NotNull SimpleLocation location); - - /** - * Remove sprinkler data from a certain location - * - * @param location location - */ - @Nullable - WorldSprinkler removeSprinklerAt(@NotNull SimpleLocation location); - - /** - * Remove pot data from a certain location - * - * @param location location - */ - @Nullable - WorldPot removePotAt(@NotNull SimpleLocation location); - - /** - * Remove crop data from a certain location - * - * @param location location - */ - @Nullable - WorldCrop removeCropAt(@NotNull SimpleLocation location); - - /** - * Remove greenhouse glass data from a certain location - * - * @param location location - */ - @Nullable - WorldGlass removeGlassAt(@NotNull SimpleLocation location); - - /** - * Remove scarecrow data from a certain location - * - * @param location location - */ - @Nullable - WorldScarecrow removeScarecrowAt(@NotNull SimpleLocation location); - - /** - * If a certain type of item reached the limitation - * - * @param location location - * @param itemType the type of the item - * @return reached or not - */ - boolean isReachLimit(SimpleLocation location, ItemType itemType); - - /** - * Handle the load of a chunk - * It's recommended to call world.getChunkAt(x,z), otherwise you have to manually control the load/unload process - * - * @param bukkitChunk chunk - */ - void handleChunkLoad(Chunk bukkitChunk); - - /** - * Handle the unload of a chunk - * - * @param bukkitChunk chunk - */ - void handleChunkUnload(Chunk bukkitChunk); - - /** - * Save a chunk to region (from memory to memory) - * - * @param chunk the chunk to save - */ - void saveChunkToCachedRegion(CustomCropsChunk chunk); - - /** - * Save a region to file (from memory to disk) - * - * @param region the region to save - */ - void saveRegionToFile(CustomCropsRegion region); - - /** - * Remove any block data from a certain location - * - * @param location location - * @return block data - */ - CustomCropsBlock removeAnythingAt(SimpleLocation location); - - /** - * Get the world adaptor - * - * @return the world adaptor - */ - AbstractWorldAdaptor getWorldAdaptor(); - - /** - * Save a world's season and date - * - * @param customCropsWorld the world to save - */ - void saveInfoData(CustomCropsWorld customCropsWorld); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java deleted file mode 100644 index fde774efe..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.action; - -import net.momirealms.customcrops.api.mechanic.requirement.State; - -public interface Action { - - /** - * Trigger the action - * - * @param state the state of the player - */ - void trigger(State state); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java deleted file mode 100644 index 9f9f9a3dc..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.action; - -public abstract class ActionExpansion { - - /** - * Get the version number - * - * @return version - */ - public abstract String getVersion(); - - /** - * Get the author - * - * @return author - */ - public abstract String getAuthor(); - - /** - * Get the type of the action - * - * @return the type of the action - */ - public abstract String getActionType(); - - /** - * Get the action factory - * - * @return the action factory - */ - public abstract ActionFactory getActionFactory(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionTrigger.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionTrigger.java deleted file mode 100644 index 4ae87a6f3..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionTrigger.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.action; - -public enum ActionTrigger { - - BREAK, - PLACE, - GROW, - ADD_WATER, - NO_WATER, - CONSUME_WATER, - FULL, - WORK, - USE, - WRONG_POT, - WRONG_SPRINKLER, - BEFORE_PLANT, - REACH_LIMIT, - INTERACT, PLANT, RIPE, -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java deleted file mode 100644 index 1f38f85e8..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.condition; - -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; - -public interface Condition { - - /** - * If the block meets the conditions - * - * @param block block - * @param offline offline tick - * @return met or not - */ - boolean isConditionMet(CustomCropsBlock block, boolean offline); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java deleted file mode 100644 index 0ecde8a5c..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.condition; - -public abstract class ConditionExpansion { - - /** - * Get the version number - * - * @return version - */ - public abstract String getVersion(); - - /** - * Get the author - * - * @return author - */ - public abstract String getAuthor(); - - /** - * Get the type of the condition - * - * @return the type of the condition - */ - public abstract String getConditionType(); - - /** - * Get the condition factory - * - * @return the condition factory - */ - public abstract ConditionFactory getConditionFactory(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java deleted file mode 100644 index 97beecea6..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.condition; - -public interface ConditionFactory { - - /** - * Build a condition with the args - * - * @param args args - * @return condition - */ - Condition build(Object args); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java deleted file mode 100644 index aa38313ba..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.customcrops.api.mechanic.condition; - -public class Conditions { - - private final Condition[] conditions; - - public Conditions(Condition[] conditions) { - this.conditions = conditions; - } - - /** - * Get a list of conditions - * - * @return conditions - */ - public Condition[] getConditions() { - return conditions; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java deleted file mode 100644 index 7dcc29e32..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java +++ /dev/null @@ -1,46 +0,0 @@ -package net.momirealms.customcrops.api.mechanic.condition; - -import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; -import org.jetbrains.annotations.Nullable; - -public class DeathConditions extends Conditions { - - private final String deathItem; - private final ItemCarrier itemCarrier; - private final int deathDelay; - - public DeathConditions(Condition[] conditions, String deathItem, ItemCarrier itemCarrier, int deathDelay) { - super(conditions); - this.deathItem = deathItem; - this.itemCarrier = itemCarrier; - this.deathDelay = deathDelay; - } - - /** - * Get the item to replace, null if the crop would be removed - * - * @return the item to replace - */ - @Nullable - public String getDeathItem() { - return deathItem; - } - - /** - * Get the item carrier of the item to replace - * - * @return item carrier - */ - public ItemCarrier getItemCarrier() { - return itemCarrier; - } - - /** - * Get the delay in ticks - * - * @return delay - */ - public int getDeathDelay() { - return deathDelay; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java deleted file mode 100644 index 4a942f1e0..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -import net.momirealms.customcrops.api.common.item.KeyItem; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.condition.Conditions; -import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.HashSet; - -public interface Crop extends KeyItem { - - /** - * Get the id of the seed - * - * @return seed ID - */ - String getSeedItemID(); - - /** - * Get the max points to grow - * - * @return max points - */ - int getMaxPoints(); - - /** - * Get the requirements for planting - * - * @return requirements for planting - */ - Requirement[] getPlantRequirements(); - - /** - * Get the requirements for breaking - * - * @return requirements for breaking - */ - Requirement[] getBreakRequirements(); - - /** - * Get the requirements for interactions - * - * @return requirements for interactions - */ - Requirement[] getInteractRequirements(); - - /** - * Get the conditions to grow - * - * @return conditions to grow - */ - Conditions getGrowConditions(); - - /** - * Get the conditions of death - * - * @return conditions of death - */ - DeathConditions[] getDeathConditions(); - - /** - * Get the available bone meals - * - * @return bone meals - */ - BoneMeal[] getBoneMeals(); - - /** - * If the crop has rotations - */ - boolean hasRotation(); - - /** - * Trigger actions - * - * @param trigger action trigger - * @param state player state - */ - void trigger(ActionTrigger trigger, State state); - - /** - * Get the stage config by point - * - * @param point point - * @return stage config - */ - @Nullable - Stage getStageByPoint(int point); - - /** - * Get the stage item ID by point - * This is always NotNull if the point is no lower than 0 - * - * @param point point - * @return the stage item ID - */ - @NotNull - String getStageItemByPoint(int point); - - /** - * Get stage config by stage item ID - * - * @param id item id - * @return stage config - */ - @Nullable - Stage getStageByItemID(String id); - - /** - * Get all the stages - * - * @return stages - */ - Collection getStages(); - - /** - * Get the pots to plant - * - * @return whitelisted pots - */ - HashSet getPotWhitelist(); - - /** - * Get the carrier of this crop - * - * @return carrier of this crop - */ - ItemCarrier getItemCarrier(); - - interface Stage { - - /** - * Get the crop config - * - * @return crop config - */ - Crop getCrop(); - - /** - * Get the offset of the hologram - * - * @return offset - */ - double getHologramOffset(); - - /** - * Get the stage item ID - * This can be null if this point doesn't have any state change - * - * @return stage item ID - */ - @Nullable - String getStageID(); - - /** - * Get the point of this stage - * - * @return point - */ - int getPoint(); - - /** - * Trigger actions - * - * @param trigger action trigger - * @param state player state - */ - void trigger(ActionTrigger trigger, State state); - - /** - * Get the requirements for interactions - * - * @return requirements for interactions - */ - Requirement[] getInteractRequirements(); - - /** - * Get the requirements for breaking - * - * @return requirements for breaking - */ - Requirement[] getBreakRequirements(); - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java deleted file mode 100644 index 17d50c776..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -import net.momirealms.customcrops.api.common.item.EventItem; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; - -import java.util.HashSet; - -public interface Fertilizer extends EventItem { - - /** - * Get the key - * - * @return key - */ - String getKey(); - - /** - * Get the item ID - * - * @return item ID - */ - String getItemID(); - - /** - * Get the max times of usage - * - * @return the max times of usage - */ - int getTimes(); - - /** - * Get the type of the fertilizer - * - * @return the type of the fertilizer - */ - FertilizerType getFertilizerType(); - - /** - * Get the pot whitelist - * - * @return pot whitelist - */ - HashSet getPotWhitelist(); - - /** - * If the fertilizer can only be used before planting - */ - boolean isBeforePlant(); - - /** - * Get the image of the fertilizer - * - * @return icon - */ - String getIcon(); - - /** - * Get the requirements for this fertilizer - * - * @return requirements - */ - Requirement[] getRequirements(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/FertilizerType.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/FertilizerType.java deleted file mode 100644 index 2e6898cee..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/FertilizerType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -public enum FertilizerType { - SPEED_GROW, - QUALITY, - SOIL_RETAIN, - VARIATION, - YIELD_INCREASE -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemCarrier.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemCarrier.java deleted file mode 100644 index 449d7101b..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemCarrier.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -public enum ItemCarrier { - NOTE_BLOCK, - MUSHROOM, - CHORUS, - TRIPWIRE, - ITEM_FRAME, - ITEM_DISPLAY -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemType.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemType.java deleted file mode 100644 index b28b3e826..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -public enum ItemType { - CROP, - POT, - SPRINKLER, - GREENHOUSE, - SCARECROW -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java deleted file mode 100644 index 2a1628b45..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -import net.momirealms.customcrops.api.common.item.KeyItem; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; -import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.requirement.State; - -import java.util.HashSet; - -public interface Pot extends KeyItem { - - /** - * Get max water storage - * - * @return water storage - */ - int getStorage(); - - /** - * Get the key - * - * @return key - */ - String getKey(); - - /** - * Get the blocks that belong to this pot - * - * @return blocks - */ - HashSet getPotBlocks(); - - /** - * Get the methods to fill this pot - * - * @return methods - */ - PassiveFillMethod[] getPassiveFillMethods(); - - /** - * Get the dry state - * - * @return dry state item ID - */ - String getDryItem(); - - /** - * Get the wet state - * - * @return wet state item ID - */ - String getWetItem(); - - /** - * Get the requirements for placement - * - * @return requirements for placement - */ - Requirement[] getPlaceRequirements(); - - /** - * Get the requirements for breaking - * - * @return requirements for breaking - */ - Requirement[] getBreakRequirements(); - - /** - * Get the requirements for using - * - * @return requirements for using - */ - Requirement[] getUseRequirements(); - - /** - * Trigger actions - * - * @param trigger action trigger - * @param state player state - */ - void trigger(ActionTrigger trigger, State state); - - /** - * Get the water bar images - * - * @return water bar images - */ - WaterBar getWaterBar(); - - /** - * Does the pot absorb raindrop - */ - boolean isRainDropAccepted(); - - /** - * Does nearby water make the pot wet - */ - boolean isNearbyWaterAccepted(); - - /** - * Get the block ID by water and fertilizers - * - * @param water water - * @param type the type of the fertilizer - * @return block item ID - */ - String getBlockState(boolean water, FertilizerType type); - - /** - * Is the pot a vanilla block - */ - boolean isVanillaBlock(); - - /** - * Is the id a wet pot - */ - boolean isWetPot(String id); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java deleted file mode 100644 index e9667aa40..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -import net.momirealms.customcrops.api.common.item.KeyItem; - -public interface Scarecrow extends KeyItem { - - /** - * Get the item ID - * - * @return item ID - */ - String getItemID(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java deleted file mode 100644 index f34148a1c..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -import net.momirealms.customcrops.api.common.item.KeyItem; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; -import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.requirement.State; - -import java.util.HashSet; - -public interface Sprinkler extends KeyItem { - - /** - * Get the 2D item ID - * - * @return 2D item ID - */ - String get2DItemID(); - - /** - * Get the 3D item ID - * - * @return 3D item ID - */ - String get3DItemID(); - - /** - * Get the 3D item ID (With water inside) - * - * @return 3D item ID (With water inside) - */ - String get3DItemWithWater(); - - /** - * Get the max storage of water - * - * @return max storage of water - */ - int getStorage(); - - /** - * Get the working range - * - * @return working range - */ - int getRange(); - - /** - * Is water infinite - */ - boolean isInfinite(); - - /** - * Get the amount of water to add to the pot during sprinkling - * - * @return amount of water to add to the pot during sprinkling - */ - int getWater(); - - /** - * Get the pots that receive the water - * - * @return whitelisted pots - */ - HashSet getPotWhitelist(); - - /** - * Get the carrier of the pot - * - * @return carrier of the pot - */ - ItemCarrier getItemCarrier(); - - /** - * Get methods to fill the sprinkler - * - * @return methods to fill the sprinkler - */ - PassiveFillMethod[] getPassiveFillMethods(); - - /** - * Get the requirements for placement - * - * @return requirements for placement - */ - Requirement[] getPlaceRequirements(); - - /** - * Get the requirements for breaking - * - * @return requirements for breaking - */ - Requirement[] getBreakRequirements(); - - /** - * Get the requirements for using - * - * @return requirements for using - */ - Requirement[] getUseRequirements(); - - /** - * Trigger actions - * - * @param trigger action trigger - * @param state player state - */ - void trigger(ActionTrigger trigger, State state); - - /** - * Get the water bar images - * - * @return water bar images - */ - WaterBar getWaterBar(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java deleted file mode 100644 index fccad507d..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -import net.momirealms.customcrops.api.common.item.KeyItem; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -public interface WateringCan extends KeyItem { - - /** - * Get the ID of the item - * - * @return item ID - */ - String getItemID(); - - /** - * Get the width of the effective range - * - * @return width - */ - int getWidth(); - - /** - * Get the length of the effective range - * - * @return length - */ - int getLength(); - - /** - * Get the storage of water - * - * @return storage of water - */ - int getStorage(); - - /** - * Get the amount of water to add in one try - */ - int getWater(); - - /** - * If the watering can has dynamic lore - */ - boolean hasDynamicLore(); - - /** - * Update a watering can's data - * - * @param player player - * @param itemStack watering can item - * @param water the amount of water - * @param args the placeholders - */ - void updateItem(Player player, ItemStack itemStack, int water, Map args); - - /** - * Get the current water - * - * @param itemStack watering can item - * @return current water - */ - int getCurrentWater(ItemStack itemStack); - - /** - * Get the pots that receive water from this watering can - * - * @return whitelisted pots - */ - HashSet getPotWhitelist(); - - /** - * Get the sprinklers that receive water from this watering can - * - * @return whitelisted sprinklers - */ - HashSet getSprinklerWhitelist(); - - /** - * Get the dynamic lores - * - * @return dynamic lores - */ - List getLore(); - - /** - * Get the water bar images - * - * @return water bar images - */ - @Nullable WaterBar getWaterBar(); - - /** - * Get the requirements for using this watering can - * - * @return requirements - */ - Requirement[] getRequirements(); - - /** - * If the water is infinite - */ - boolean isInfinite(); - - /** - * Trigger actions - * - * @param trigger action trigger - * @param state player state - */ - void trigger(ActionTrigger trigger, State state); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java deleted file mode 100644 index e841c9985..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.event.BoneMealDispenseEvent; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.ItemManager; -import net.momirealms.customcrops.api.manager.VersionManager; -import net.momirealms.customcrops.api.manager.WorldManager; -import net.momirealms.customcrops.api.mechanic.item.*; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import net.momirealms.customcrops.api.mechanic.world.level.WorldGlass; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; -import net.momirealms.customcrops.api.util.EventUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Dispenser; -import org.bukkit.entity.Entity; -import org.bukkit.entity.FallingBlock; -import org.bukkit.entity.Item; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.*; -import org.bukkit.event.entity.EntityChangeBlockEvent; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.entity.ItemSpawnEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerItemDamageEvent; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -import java.util.HashSet; -import java.util.List; -import java.util.Optional; - -public abstract class AbstractCustomListener implements Listener { - - protected ItemManager itemManager; - private final HashSet CUSTOM_MATERIAL = new HashSet<>(); - - public AbstractCustomListener(ItemManager itemManager) { - this.itemManager = itemManager; - this.CUSTOM_MATERIAL.addAll( - List.of( - Material.NOTE_BLOCK, - Material.MUSHROOM_STEM, - Material.BROWN_MUSHROOM_BLOCK, - Material.RED_MUSHROOM_BLOCK, - Material.TRIPWIRE, - Material.CHORUS_PLANT, - Material.CHORUS_FLOWER, - Material.ACACIA_LEAVES, - Material.BIRCH_LEAVES, - Material.JUNGLE_LEAVES, - Material.DARK_OAK_LEAVES, - Material.AZALEA_LEAVES, - Material.FLOWERING_AZALEA_LEAVES, - Material.OAK_LEAVES, - Material.SPRUCE_LEAVES, - Material.CAVE_VINES, - Material.TWISTING_VINES, - Material.WEEPING_VINES, - Material.KELP, - Material.CACTUS, - Material.AIR - ) - ); - if (VersionManager.isHigherThan1_19()) { - this.CUSTOM_MATERIAL.add( - Material.MANGROVE_LEAVES - ); - } - if (VersionManager.isHigherThan1_20()) { - this.CUSTOM_MATERIAL.add( - Material.CHERRY_LEAVES - ); - } - } - - @EventHandler (ignoreCancelled = true) - public void onBlockFalling(EntityChangeBlockEvent event) { - if (event.getEntity() instanceof FallingBlock fallingBlock) { - final Block block = event.getBlock(); - final Location location = block.getLocation(); - Optional customCropsBlock = CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(location)); - if (customCropsBlock.isPresent()) { - event.setCancelled(true); - block.getWorld().dropItemNaturally(block.getLocation(), new ItemStack(fallingBlock.getBlockData().getMaterial())); - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onInteractBlock(PlayerInteractEvent event) { - if (event.getHand() != EquipmentSlot.HAND) - return; - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) - return; - - Player player = event.getPlayer(); - this.itemManager.handlePlayerInteractBlock( - player, - event.getClickedBlock(), - event.getBlockFace(), - event - ); - } - - @EventHandler - public void onInteractAir(PlayerInteractEvent event) { - if (event.getHand() != EquipmentSlot.HAND) - return; - if (event.getAction() != Action.RIGHT_CLICK_AIR) - return; - - Player player = event.getPlayer(); - this.itemManager.handlePlayerInteractAir( - player, - event - ); - } - - @EventHandler (ignoreCancelled = true, priority = EventPriority.LOW) - public void onBreakBlock(BlockBreakEvent event) { - Player player = event.getPlayer(); - Block block = event.getBlock(); - Material type = block.getType(); - // custom block should be handled by other plugins' events - if (CUSTOM_MATERIAL.contains(type)) - return; - this.itemManager.handlePlayerBreakBlock( - player, - block, - type.name(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceBlock(BlockPlaceEvent event) { - final Block block = event.getBlock(); - final Location location = block.getLocation(); - if (CUSTOM_MATERIAL.contains(block.getType())) { - return; - } - Optional customCropsBlock = CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(location)); - if (customCropsBlock.isPresent()) { - CustomCropsPlugin.get().getWorldManager().removeAnythingAt(SimpleLocation.of(location)); - } - this.onPlaceBlock( - event.getPlayer(), - block, - event.getBlockPlaced().getType().name(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onItemSpawn(ItemSpawnEvent event) { - Item item = event.getEntity(); - ItemStack itemStack = item.getItemStack(); - String itemID = this.itemManager.getItemID(itemStack); - Crop.Stage stage = this.itemManager.getCropStageByStageID(itemID); - if (stage != null) { - event.setCancelled(true); - return; - } - - Sprinkler sprinkler = this.itemManager.getSprinklerBy3DItemID(itemID); - if (sprinkler != null) { - ItemStack newItem = this.itemManager.getItemStack(null, sprinkler.get2DItemID()); - if (newItem != null && newItem.getType() != Material.AIR) { - newItem.setAmount(itemStack.getAmount()); - item.setItemStack(newItem); - } - return; - } - - Pot pot = this.itemManager.getPotByBlockID(itemID); - if (pot != null) { - ItemStack newItem = this.itemManager.getItemStack(null, pot.getDryItem()); - if (newItem != null && newItem.getType() != Material.AIR) { - newItem.setAmount(itemStack.getAmount()); - item.setItemStack(newItem); - } - return; - } - } - - @EventHandler (ignoreCancelled = true) - public void onBlockChange(BlockFadeEvent event) { - Block block = event.getBlock(); - if (block.getType() == Material.FARMLAND) { - SimpleLocation above = SimpleLocation.of(block.getLocation()).add(0,1,0); - if (CustomCropsPlugin.get().getWorldManager().getBlockAt(above).isPresent()) { - event.setCancelled(true); - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onTrampling(EntityChangeBlockEvent event) { - Block block = event.getBlock(); - if (block.getType() == Material.FARMLAND && event.getTo() == Material.DIRT) { - if (ConfigManager.preventTrampling()) { - event.setCancelled(true); - return; - } - itemManager.handleEntityTramplingBlock(event.getEntity(), block, event); - } - } - - @EventHandler (ignoreCancelled = true) - public void onMoistureChange(MoistureChangeEvent event) { - if (ConfigManager.disableMoisture()) - event.setCancelled(true); - } - - @EventHandler (ignoreCancelled = true) - public void onPistonExtend(BlockPistonExtendEvent event) { - WorldManager manager = CustomCropsPlugin.get().getWorldManager(); - for (Block block : event.getBlocks()) { - if (manager.getBlockAt(SimpleLocation.of(block.getLocation())).isPresent()) { - event.setCancelled(true); - return; - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onPistonRetract(BlockPistonRetractEvent event) { - WorldManager manager = CustomCropsPlugin.get().getWorldManager(); - for (Block block : event.getBlocks()) { - if (manager.getBlockAt(SimpleLocation.of(block.getLocation())).isPresent()) { - event.setCancelled(true); - return; - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onItemDamage(PlayerItemDamageEvent event) { - ItemStack itemStack = event.getItem(); - WateringCan wateringCan = this.itemManager.getWateringCanByItemStack(itemStack); - if (wateringCan != null) { - event.setCancelled(true); - } - } - - @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onExplosion(EntityExplodeEvent event) { - this.itemManager.handleExplosion(event.getEntity(), event.blockList(), event); - } - - @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onExplosion(BlockExplodeEvent event) { - this.itemManager.handleExplosion(null, event.blockList(), event); - } - - @EventHandler (ignoreCancelled = true) - public void onDispenser(BlockDispenseEvent event) { - Block block = event.getBlock(); - if (block.getBlockData() instanceof org.bukkit.block.data.type.Dispenser directional) { - Block relative = block.getRelative(directional.getFacing()); - Location location = relative.getLocation(); - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional worldCropOptional = CustomCropsPlugin.get().getWorldManager().getCropAt(simpleLocation); - if (worldCropOptional.isPresent()) { - WorldCrop crop = worldCropOptional.get(); - Crop config = crop.getConfig(); - ItemStack itemStack = event.getItem(); - String itemID = itemManager.getItemID(itemStack); - if (crop.getPoint() < config.getMaxPoints()) { - for (BoneMeal boneMeal : config.getBoneMeals()) { - if (boneMeal.getItem().equals(itemID)) { - if (!boneMeal.isDispenserAllowed()) { - return; - } - // fire the event - if (EventUtils.fireAndCheckCancel(new BoneMealDispenseEvent(block, itemStack, location, boneMeal, crop))) { - event.setCancelled(true); - return; - } - if (block.getState() instanceof Dispenser dispenser) { - event.setCancelled(true); - Inventory inventory = dispenser.getInventory(); - for (ItemStack storage : inventory.getStorageContents()) { - if (storage == null) continue; - String id = itemManager.getItemID(storage); - if (id.equals(itemID)) { - storage.setAmount(storage.getAmount() - 1); - boneMeal.trigger(new State(null, itemStack, location)); - CustomCropsPlugin.get().getWorldManager().addPointToCrop(config, boneMeal.getPoint(), simpleLocation); - } - } - } - return; - } - } - } - } - } - } - - public void onPlaceBlock(Player player, Block block, String blockID, Cancellable event) { - if (player == null) return; - this.itemManager.handlePlayerPlaceBlock(player, block, blockID, event); - } - - public void onBreakFurniture(Player player, Location location, String id, Cancellable event) { - if (player == null) return; - this.itemManager.handlePlayerBreakFurniture(player, location, id, event); - } - - public void onPlaceFurniture(Player player, Location location, String id, Cancellable event) { - if (player == null) return; - this.itemManager.handlePlayerPlaceFurniture(player, location, id, event); - } - - public void onInteractFurniture(Player player, Location location, String id, @Nullable Entity baseEntity, Cancellable event) { - if (player == null) return; - this.itemManager.handlePlayerInteractFurniture(player, location, id, baseEntity, event); - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java deleted file mode 100644 index c6029ea16..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/CustomProvider.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom; - -import net.momirealms.customcrops.api.manager.VersionManager; -import net.momirealms.customcrops.api.mechanic.misc.CRotation; -import net.momirealms.customcrops.api.util.DisplayEntityUtils; -import net.momirealms.customcrops.api.util.LocationUtils; -import net.momirealms.customcrops.api.util.StringUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.*; -import org.bukkit.inventory.ItemStack; - -import java.util.Collection; - -public interface CustomProvider { - - boolean removeBlock(Location location); - - void placeCustomBlock(Location location, String id); - - default void placeBlock(Location location, String id) { - if (StringUtils.isCapitalLetter(id)) { - location.getBlock().setType(Material.valueOf(id)); - } else { - placeCustomBlock(location, id); - } - } - - Entity placeFurniture(Location location, String id); - - void removeFurniture(Entity entity); - - String getBlockID(Block block); - - String getItemID(ItemStack itemInHand); - - ItemStack getItemStack(String id); - - String getEntityID(Entity entity); - - boolean isFurniture(Entity entity); - - default boolean isAir(Location location) { - Block block = location.getBlock(); - if (block.getType() != Material.AIR) - return false; - Location center = LocationUtils.toCenterLocation(location); - Collection entities = center.getWorld().getNearbyEntities(center, 0.5,0.51,0.5); - entities.removeIf(entity -> (entity instanceof Player || entity instanceof Item)); - return entities.isEmpty(); - } - - default CRotation removeAnythingAt(Location location) { - removeBlock(location); - Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toCenterLocation(location), 0.5,0.51,0.5); - entities.removeIf(entity -> { - EntityType type = entity.getType(); - return type != EntityType.ITEM_FRAME - && (!VersionManager.isHigherThan1_19_R3() || type != EntityType.ITEM_DISPLAY); - }); - if (entities.isEmpty()) return CRotation.NONE; - CRotation previousCRotation; - Entity first = entities.stream().findFirst().get(); - if (first instanceof ItemFrame itemFrame) { - previousCRotation = CRotation.getByRotation(itemFrame.getRotation()); - } else if (VersionManager.isHigherThan1_19_R3()) { - previousCRotation = DisplayEntityUtils.getRotation(first); - } else { - previousCRotation = CRotation.NONE; - } - for (Entity entity : entities) { - removeFurniture(entity); - } - return previousCRotation; - } - - default String getSomethingAt(Location location) { - Block block = location.getBlock(); - if (block.getType() != Material.AIR) { - return getBlockID(block); - } else { - Collection entities = location.getWorld().getNearbyEntities(location.toCenterLocation(), 0.5,0.51,0.5); - for (Entity entity : entities) { - if (isFurniture(entity)) { - return getEntityID(entity); - } - } - } - return "AIR"; - } - - default CRotation getRotation(Location location) { - if (location.getBlock().getType() == Material.AIR) { - Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toCenterLocation(location), 0.5,0.51,0.5); - entities.removeIf(entity -> { - EntityType type = entity.getType(); - return type != EntityType.ITEM_FRAME - && (!VersionManager.isHigherThan1_19_R3() || type != EntityType.ITEM_DISPLAY); - }); - if (entities.isEmpty()) return CRotation.NONE; - CRotation rotation; - Entity first = entities.stream().findFirst().get(); - if (first instanceof ItemFrame itemFrame) { - rotation = CRotation.getByRotation(itemFrame.getRotation()); - } else if (VersionManager.isHigherThan1_19_R3()) { - rotation = DisplayEntityUtils.getRotation(first); - } else { - rotation = CRotation.NONE; - } - return rotation; - } - return CRotation.NONE; - } -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java deleted file mode 100644 index 28a20b12d..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.fertilizer; - -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; - -public interface SpeedGrow extends Fertilizer { - - /** - * Get the extra points to gain - * - * @return points - */ - int getPointBonus(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java deleted file mode 100644 index fbdfc5125..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.fertilizer; - -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; - -public interface YieldIncrease extends Fertilizer { - - /** - * Get the extra amount to drop - * - * @return amount bonus - */ - int getAmountBonus(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/AbstractFillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/AbstractFillMethod.java deleted file mode 100644 index 6855f91e2..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/AbstractFillMethod.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.water; - -import net.momirealms.customcrops.api.manager.ActionManager; -import net.momirealms.customcrops.api.manager.RequirementManager; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.requirement.State; - -public abstract class AbstractFillMethod { - - protected int amount; - private final Action[] actions; - private final Requirement[] requirements; - - protected AbstractFillMethod(int amount, Action[] actions, Requirement[] requirements) { - this.amount = amount; - this.actions = actions; - this.requirements = requirements; - } - - /** - * Get the amount of water to add - * - * @return amount - */ - public int getAmount() { - return amount; - } - - /** - * Trigger actions related to this fill methods - * - * @param state state - */ - public void trigger(State state) { - ActionManager.triggerActions(state, actions); - } - - /** - * If player meet the requirements for this fill method - * - * @param state state - * @return meet or not - */ - public boolean canFill(State state) { - return RequirementManager.isRequirementMet(state, requirements); - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/CRotation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/CRotation.java deleted file mode 100644 index 1e1fc4c5c..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/CRotation.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.momirealms.customcrops.api.mechanic.misc; - -import org.bukkit.Rotation; - -public enum CRotation { - - NONE(0f), - RANDOM(0f), - EAST(-90f), - SOUTH(0f), - WEST(90f), - NORTH(180f); - - private final float yaw; - - CRotation(float yaw) { - this.yaw = yaw; - } - - public float getYaw() { - return yaw; - } - - public static CRotation getByRotation(Rotation rotation) { - switch (rotation) { - default -> { - return CRotation.NONE; - } - case CLOCKWISE -> { - return CRotation.WEST; - } - case COUNTER_CLOCKWISE -> { - return CRotation.EAST; - } - case FLIPPED -> { - return CRotation.NORTH; - } - } - } - - public static CRotation getByYaw(float yaw) { - yaw = (Math.abs(yaw + 180) % 360); - switch ((int) (yaw/90)) { - case 1 -> { - return CRotation.WEST; - } - case 2 -> { - return CRotation.NORTH; - } - case 3 -> { - return CRotation.EAST; - } - default -> { - return CRotation.SOUTH; - } - } - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/MatchRule.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/MatchRule.java deleted file mode 100644 index 8258d7293..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/MatchRule.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.customcrops.api.mechanic.misc; - -public enum MatchRule { - - BLACKLIST, - WHITELIST, - REGEX -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Reason.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Reason.java deleted file mode 100644 index eae238659..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Reason.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.misc; - -public enum Reason { - // Player break - BREAK, - // Trampling - TRAMPLE, - // Block explosion and entity explosion - EXPLODE, - // Action "break" - ACTION -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java deleted file mode 100644 index 48e180205..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.misc; - -import org.bukkit.entity.Player; - -public interface Value { - - /** - * Get double value - * - * @param player player - * @return the value - */ - double get(Player player); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java deleted file mode 100644 index e92876c9b..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.requirement; - -public interface Requirement { - - /** - * Does player meet the requirement - * - * @param state state of player - * @return met or not - */ - boolean isStateMet(State state); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java deleted file mode 100644 index 4fa5eec7b..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.requirement; - -import net.momirealms.customcrops.api.mechanic.action.Action; - -import java.util.List; - -public interface RequirementFactory { - - /** - * Build a requirement - * - * @param args args - * @param notMetActions actions to perform if the requirement is not met - * @param advanced whether to trigger the notMetActions or not - * @return requirement - */ - Requirement build(Object args, List notMetActions, boolean advanced); - - /** - * Build a requirement - * - * @param args args - * @return requirement - */ - default Requirement build(Object args) { - return build(args, null, false); - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java deleted file mode 100644 index 630a49a0f..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.requirement; - -import net.momirealms.customcrops.api.util.LocationUtils; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; -import java.util.Map; - -public class State { - - private final Player player; - private final ItemStack itemInHand; - private final Location location; - private final HashMap args; - - public State(Player player, ItemStack itemInHand, @NotNull Location location) { - this.player = player; - this.itemInHand = itemInHand; - this.location = LocationUtils.toBlockLocation(location); - this.args = new HashMap<>(); - if (player != null) { - setArg("{player}", player.getName()); - } - setArg("{x}", String.valueOf(location.getBlockX())); - setArg("{y}", String.valueOf(location.getBlockY())); - setArg("{z}", String.valueOf(location.getBlockZ())); - setArg("{world}", location.getWorld().getName()); - } - - public Player getPlayer() { - return player; - } - - public ItemStack getItemInHand() { - return itemInHand; - } - - public Location getLocation() { - return location; - } - - public Map getArgs() { - return args; - } - - public void setArg(String key, String value) { - args.put(key, value); - } - - public String getArg(String key) { - return args.get(key); - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/AbstractWorldAdaptor.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/AbstractWorldAdaptor.java deleted file mode 100644 index 8827d2dba..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/AbstractWorldAdaptor.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import net.momirealms.customcrops.api.manager.WorldManager; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsRegion; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; -import org.bukkit.event.Listener; - -public abstract class AbstractWorldAdaptor implements Listener { - - public static final int chunkVersion = 1; - public static final int regionVersion = 1; - protected WorldManager worldManager; - - public AbstractWorldAdaptor(WorldManager worldManager) { - this.worldManager = worldManager; - } - - public abstract void unload(CustomCropsWorld customCropsWorld); - - public abstract void init(CustomCropsWorld customCropsWorld); - - public abstract void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos); - - public abstract void unloadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos); - - public abstract void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk); - - public abstract void saveRegion(CustomCropsRegion customCropsRegion); - - public abstract void saveInfoData(CustomCropsWorld customCropsWorld); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java deleted file mode 100644 index 1aae7f8f3..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import net.momirealms.customcrops.api.mechanic.item.ItemType; -import net.momirealms.customcrops.api.mechanic.world.level.DataBlock; - -public interface CustomCropsBlock extends DataBlock, Tickable { - - /** - * Get the type of the item - * - * @return type of the item - */ - ItemType getType(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java deleted file mode 100644 index 14c623d15..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; - -public class SimpleLocation { - - private int x; - private int y; - private int z; - private final String worldName; - - public SimpleLocation(String worldName, int x, int y, int z){ - this.worldName = worldName; - this.x = x; - this.y = y; - this.z = z; - } - - public int getX() { - return x; - } - - public int getZ() { - return z; - } - - public int getY() { - return y; - } - - public String getWorldName() { - return worldName; - } - - public ChunkPos getChunkPos() { - return new ChunkPos((int) Math.floor((double) getX() / 16), (int) Math.floor((double) getZ() / 16)); - } - - public SimpleLocation add(int x, int y, int z) { - this.x += x; - this.y += y; - this.z += z; - return this; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final SimpleLocation other = (SimpleLocation) obj; - if (!Objects.equals(worldName, other.getWorldName())) { - return false; - } - if (this.x != other.x) { - return false; - } - if (this.y != other.y) { - return false; - } - if (this.z != other.z) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int hash = 3; - hash = 7 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); - hash = 13 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32)); - return hash; - } - - public Location getBukkitLocation() { - World world = Bukkit.getWorld(worldName); - if (world == null) return null; - return new Location(world, x, y, z); - } - - @Nullable - public World getBukkitWorld() { - return Bukkit.getWorld(worldName); - } - - public static SimpleLocation getByString(String location) { - String[] loc = location.split(",", 4); - return new SimpleLocation(loc[0], Integer.parseInt(loc[1]), Integer.parseInt(loc[2]), Integer.parseInt(loc[3])); - } - - public static SimpleLocation of(Location location) { - return new SimpleLocation(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()); - } - - @Override - public String toString() { - return "SimpleLocation{" + - "x=" + x + - ", y=" + y + - ", z=" + z + - ", worldName='" + worldName + '\'' + - '}'; - } - - public boolean isNear(SimpleLocation simpleLocation, int distance) { - if (Math.abs(simpleLocation.x - this.x) > distance) { - return false; - } - if (Math.abs(simpleLocation.z - this.z) > distance) { - return false; - } - if (Math.abs(simpleLocation.y - this.y) > distance) { - return false; - } - return true; - } - - public SimpleLocation copy() { - return new SimpleLocation(worldName, x, y, z); - } -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java deleted file mode 100644 index 031d04593..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.momirealms.customcrops.api.mechanic.world; - -public interface Tickable { - - /** - * Tick - * - * @param interval interval - * @param offline offline tick - */ - void tick(int interval, boolean offline); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java deleted file mode 100644 index 1e9443397..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.IntTag; -import com.flowpowered.nbt.Tag; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.SynchronizedCompoundMap; - -public class AbstractCustomCropsBlock implements DataBlock { - - private final SimpleLocation location; - private final SynchronizedCompoundMap compoundMap; - - public AbstractCustomCropsBlock(SimpleLocation location, CompoundMap compoundMap) { - this.compoundMap = new SynchronizedCompoundMap(compoundMap); - this.location = location; - } - - /** - * Set data by key - * - * @param key key - * @param tag data tag - */ - @Override - public void setData(String key, Tag tag) { - compoundMap.put(key, tag); - } - - /** - * Get data tag by key - * - * @param key key - * @return data tag - */ - @Override - public Tag getData(String key) { - return compoundMap.get(key); - } - - /** - * Get the data map - * - * @return data map - */ - @Override - public SynchronizedCompoundMap getCompoundMap() { - return compoundMap; - } - - /** - * Get the location of the block - * - * @return location - */ - @Override - public SimpleLocation getLocation() { - return location; - } - - /** - * interval is customized - * You can perform tick logics if the block is ticked for some times - * - * @param interval interval - * @return can be ticked or not - */ - public boolean canTick(int interval) { - if (interval <= 0) { - return false; - } - if (interval == 1) { - return true; - } - Tag tag = getData("tick"); - int tick = 0; - if (tag != null) { - tick = tag.getAsIntTag().map(IntTag::getValue).orElse(0); - } - if (++tick >= interval) { - setData("tick", new IntTag("tick", 0)); - return true; - } else { - setData("tick", new IntTag("tick", tick)); - } - return false; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java deleted file mode 100644 index 19acd36ca..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.item.Crop; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.item.Pot; -import net.momirealms.customcrops.api.mechanic.item.Sprinkler; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import org.jetbrains.annotations.Nullable; - -import java.util.Optional; - -public interface CustomCropsChunk { - - /** - * Calculate the unload time and perform compensation - * This method can only be called in a loaded chunk - */ - void notifyOfflineUpdates(); - - /** - * Get the world associated with the chunk - * - * @return CustomCrops world - */ - CustomCropsWorld getCustomCropsWorld(); - - /** - * Get the region associated with the chunk - * - * @return CustomCrops region - */ - CustomCropsRegion getCustomCropsRegion(); - - /** - * Get the position of the chunk - * - * @return chunk position - */ - ChunkPos getChunkPos(); - - /** - * Do second timer - */ - void secondTimer(); - - /** - * Get the unloaded time in seconds - * This value would increase if the chunk is lazy - * - * @return the unloaded time - */ - int getUnloadedSeconds(); - - /** - * Set the unloaded seconds - * - * @param unloadedSeconds unloadedSeconds - */ - void setUnloadedSeconds(int unloadedSeconds); - - /** - * Get the last loaded time - * - * @return last loaded time - */ - long getLastLoadedTime(); - - /** - * Set the last loaded time to latest - */ - void updateLastLoadedTime(); - - /** - * Get the loaded time in seconds - * - * @return loaded time - */ - int getLoadedSeconds(); - - /** - * Get crop at a certain location - * - * @param location location - * @return crop data - */ - Optional getCropAt(SimpleLocation location); - - /** - * Get sprinkler at a certain location - * - * @param location location - * @return sprinkler data - */ - Optional getSprinklerAt(SimpleLocation location); - - /** - * Get pot at a certain location - * - * @param location location - * @return pot data - */ - Optional getPotAt(SimpleLocation location); - - /** - * Get greenhouse glass at a certain location - * - * @param location location - * @return greenhouse glass data - */ - Optional getGlassAt(SimpleLocation location); - - /** - * Get scarecrow at a certain location - * - * @param location location - * @return scarecrow data - */ - Optional getScarecrowAt(SimpleLocation location); - - /** - * Get block data at a certain location - * - * @param location location - * @return block data - */ - Optional getBlockAt(SimpleLocation location); - - /** - * Add water to the sprinkler - * This method would create new sprinkler data if the sprinkler data not exists in that place - * This method would also update the sprinkler's model if it has models according to the water amount - * - * @param sprinkler sprinkler config - * @param amount amount of water - * @param location location - */ - void addWaterToSprinkler(Sprinkler sprinkler, int amount, SimpleLocation location); - - /** - * Add fertilizer to the pot - * This method would create new pot data if the pot data not exists in that place - * This method would update the pot's block state if it has appearance variations for different fertilizers - * - * @param pot pot config - * @param fertilizer fertilizer config - * @param location location - */ - void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location); - - /** - * Add water to the pot - * This method would create new pot data if the pot data not exists in that place - * This method would update the pot's block state if it's dry - * - * @param pot pot config - * @param amount amount of water - * @param location location - */ - void addWaterToPot(Pot pot, int amount, SimpleLocation location); - - /** - * Add points to a crop - * This method would do nothing if the crop data not exists in that place - * This method would change the stage of the crop and trigger the actions - * - * @param crop crop config - * @param points points to add - * @param location location - */ - void addPointToCrop(Crop crop, int points, SimpleLocation location); - - /** - * Remove sprinkler data from a certain location - * - * @param location location - */ - @Nullable - WorldSprinkler removeSprinklerAt(SimpleLocation location); - - /** - * Remove pot data from a certain location - * - * @param location location - */ - @Nullable - WorldPot removePotAt(SimpleLocation location); - - /** - * Remove crop data from a certain location - * - * @param location location - */ - @Nullable - WorldCrop removeCropAt(SimpleLocation location); - - /** - * Remove greenhouse glass data from a certain location - * - * @param location location - */ - @Nullable - WorldGlass removeGlassAt(SimpleLocation location); - - /** - * Remove scarecrow data from a certain location - * - * @param location location - */ - @Nullable - WorldScarecrow removeScarecrowAt(SimpleLocation location); - - /** - * Remove any block data from a certain location - * - * @param location location - * @return block data - */ - @Nullable - CustomCropsBlock removeBlockAt(SimpleLocation location); - - /** - * Add a custom block data at a certain location - * - * @param block block data - * @param location location - * @return the previous block data - */ - @Nullable - CustomCropsBlock addBlockAt(CustomCropsBlock block, SimpleLocation location); - - /** - * Get the amount of crops in this chunk - * - * @return the amount of crops - */ - int getCropAmount(); - - /** - * Get the amount of pots in this chunk - * - * @return the amount of pots - */ - int getPotAmount(); - - /** - * Get the amount of sprinklers in this chunk - * - * @return the amount of sprinklers - */ - int getSprinklerAmount(); - - /** - * Add pot data - * - * @param pot pot data - * @param location location - */ - void addPotAt(WorldPot pot, SimpleLocation location); - - /** - * Add sprinkler data - * - * @param sprinkler sprinkler data - * @param location location - */ - void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location); - - /** - * Add crop data - * - * @param crop crop data - * @param location location - */ - void addCropAt(WorldCrop crop, SimpleLocation location); - - /** - * Add greenhouse glass data - * - * @param glass glass data - * @param location location - */ - void addGlassAt(WorldGlass glass, SimpleLocation location); - - /** - * Add scarecrow data - * - * @param scarecrow scarecrow data - * @param location location - */ - void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location); - - /** - * If this chunk has scarecrow - * - * @return has or not - */ - boolean hasScarecrow(); - - /** - * Get CustomCrops sections - * - * @return sections - */ - CustomCropsSection[] getSections(); - - /** - * Get section by ID - * - * @param sectionID id - * @return section - */ - @Nullable - CustomCropsSection getSection(int sectionID); - - void resetUnloadedSeconds(); - - /** - * If the chunk can be pruned - * - * @return can be pruned or not - */ - boolean canPrune(); - - boolean isOfflineTaskNotified(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsRegion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsRegion.java deleted file mode 100644 index 9852347e2..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsRegion.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.RegionPos; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public interface CustomCropsRegion { - - /** - * Get the CustomCrops world associated with the region - * - * @return CustomCrops world - */ - CustomCropsWorld getCustomCropsWorld(); - - /** - * Get the cached chunk - * - * @param pos chunk position - * @return cached chunk in bytes - */ - byte @Nullable [] getChunkBytes(ChunkPos pos); - - /** - * Get the position of the region - * - * @return the position of the region - */ - RegionPos getRegionPos(); - - /** - * Remove a chunk by the position - * - * @param pos the position of the chunk - */ - void removeChunk(ChunkPos pos); - - /** - * Put a chunk's data to cache - * - * @param pos the position of the chunk - * @param data the serialized data - */ - void saveChunk(ChunkPos pos, byte[] data); - - /** - * Get the data to save - * - * @return the data to save - */ - Map getRegionDataToSave(); - - /** - * If the region can be pruned or not - */ - boolean canPrune(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java deleted file mode 100644 index d2fff018f..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.world.BlockPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public interface CustomCropsSection { - - /** - * Get the section ID - * - * @return section ID - */ - int getSectionID(); - - /** - * Get block at a certain position - * - * @param pos block position - * @return the block - */ - @Nullable - CustomCropsBlock getBlockAt(BlockPos pos); - - /** - * Remove a block by a certain position - * - * @param pos block position - * @return the removed block - */ - @Nullable - CustomCropsBlock removeBlockAt(BlockPos pos); - - /** - * Add block at a certain position - * - * @param pos block position - * @param block the new block - * @return the previous block - */ - @Nullable - CustomCropsBlock addBlockAt(BlockPos pos, CustomCropsBlock block); - - /** - * If the section can be pruned or not - */ - boolean canPrune(); - - /** - * Get the blocks in this section - * - * @return blocks - */ - CustomCropsBlock[] getBlocks(); - - /** - * Get the block map - * - * @return block map - */ - Map getBlockMap(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java deleted file mode 100644 index 25e1804d1..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.item.Crop; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.item.Pot; -import net.momirealms.customcrops.api.mechanic.item.Sprinkler; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.RegionPos; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import org.bukkit.World; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.Optional; - -public interface CustomCropsWorld { - - /** - * Save all the data - */ - void save(); - - /** - * Start the tick system - */ - void startTick(); - - /** - * Stop the tick system - */ - void cancelTick(); - - /** - * Check if a region is loaded - * - * @param regionPos region position - * @return loaded or not - */ - boolean isRegionLoaded(RegionPos regionPos); - - /** - * Removed lazy chunks (Delayed unloading chunks) - * - * @param chunkPos chunk position - * @return the removed lazy chunk - */ - CustomCropsChunk removeLazyChunkAt(ChunkPos chunkPos); - - /** - * Get the world's settings - * - * @return world settings - */ - WorldSetting getWorldSetting(); - - /** - * Set the world's settings - * - * @param setting settings - */ - void setWorldSetting(WorldSetting setting); - - /** - * Get all the chunks - * - * @return chunks - */ - Collection getChunkStorage(); - - /** - * Get bukkit world - * This would be null if the world is unloaded - * - * @return bukkit world - */ - @Nullable - World getWorld(); - - /** - * Get the world's name - * - * @return world's name - */ - @NotNull - String getWorldName(); - - /** - * Check if the chunk is loaded - * The chunk would only be loaded when it has CustomCrops data and being loaded by minecraft chunk system - * - * @param chunkPos chunk position - * @return loaded or not - */ - boolean isChunkLoaded(ChunkPos chunkPos); - - /** - * Create CustomCrops chunk at a certain chunk position - * This method can only be called if that chunk is loaded - * Otherwise it would fail - * - * @param chunkPos chunk position - * @return the generated CustomCrops chunk - */ - Optional getOrCreateLoadedChunkAt(ChunkPos chunkPos); - - /** - * Get the loaded CustomCrops chunk at a certain chunk position - * - * @param chunkPos chunk position - * @return CustomCrops chunk - */ - Optional getLoadedChunkAt(ChunkPos chunkPos); - - /** - * Get the loaded CustomCrops region at a certain region position - * - * @param regionPos region position - * @return CustomCrops region - */ - Optional getLoadedRegionAt(RegionPos regionPos); - - /** - * Load a CustomCrops region - * You don't need to worry about the unloading since CustomCrops would unload the region - * if it's unused - * - * @param region region - */ - void loadRegion(CustomCropsRegion region); - - /** - * Load a CustomCrops chunk - * It's unsafe to call this method. Please use world.getChunkAt instead - * - * @param chunk chunk - */ - void loadChunk(CustomCropsChunk chunk); - - /** - * Unload a CustomCrops chunk - * It's unsafe to call this method - * - * @param chunkPos chunk position - */ - void unloadChunk(ChunkPos chunkPos); - - /** - * Delete a chunk's data - * - * @param chunkPos chunk position - */ - void deleteChunk(ChunkPos chunkPos); - - /** - * Set the season and date of the world - * - * @param infoData info data - */ - void setInfoData(WorldInfoData infoData); - - /** - * Get the season and date - * This might return the wrong value if sync-seasons is enabled - * - * @return info data - */ - WorldInfoData getInfoData(); - - /** - * Get the date of the world - * This would return the reference world's date if sync-seasons is enabled - * - * @return date - */ - int getDate(); - - /** - * Get the season of the world - * This would return the reference world's season if sync-seasons is enabled - * - * @return date - */ - @Nullable - Season getSeason(); - - /** - * Get sprinkler at a certain location - * - * @param location location - * @return sprinkler data - */ - Optional getSprinklerAt(SimpleLocation location); - - /** - * Get pot at a certain location - * - * @param location location - * @return pot data - */ - Optional getPotAt(SimpleLocation location); - - /** - * Get crop at a certain location - * - * @param location location - * @return crop data - */ - Optional getCropAt(SimpleLocation location); - - /** - * Get greenhouse glass at a certain location - * - * @param location location - * @return greenhouse glass data - */ - Optional getGlassAt(SimpleLocation location); - - /** - * Get scarecrow at a certain location - * - * @param location location - * @return scarecrow data - */ - Optional getScarecrowAt(SimpleLocation location); - - /** - * Get block data at a certain location - * - * @param location location - * @return block data - */ - Optional getBlockAt(SimpleLocation location); - - /** - * Add water to the sprinkler - * This method would create new sprinkler data if the sprinkler data not exists in that place - * This method would also update the sprinkler's model if it has models according to the water amount - * - * @param sprinkler sprinkler config - * @param amount amount of water - * @param location location - */ - void addWaterToSprinkler(Sprinkler sprinkler, int amount, SimpleLocation location); - - /** - * Add fertilizer to the pot - * This method would create new pot data if the pot data not exists in that place - * This method would update the pot's block state if it has appearance variations for different fertilizers - * - * @param pot pot config - * @param fertilizer fertilizer config - * @param location location - */ - void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location); - - /** - * Add water to the pot - * This method would create new pot data if the pot data not exists in that place - * This method would update the pot's block state if it's dry - * - * @param pot pot config - * @param amount amount of water - * @param location location - */ - void addWaterToPot(Pot pot, int amount, SimpleLocation location); - - /** - * Add points to a crop - * This method would do nothing if the crop data not exists in that place - * This method would change the stage of the crop and trigger the actions - * - * @param crop crop config - * @param points points to add - * @param location location - */ - void addPointToCrop(Crop crop, int points, SimpleLocation location); - - /** - * Remove sprinkler data from a certain location - * - * @param location location - */ - @Nullable - WorldSprinkler removeSprinklerAt(SimpleLocation location); - - /** - * Remove pot data from a certain location - * - * @param location location - */ - @Nullable - WorldPot removePotAt(SimpleLocation location); - - /** - * Remove crop data from a certain location - * - * @param location location - */ - @Nullable - WorldCrop removeCropAt(SimpleLocation location); - - /** - * Remove greenhouse glass data from a certain location - * - * @param location location - */ - @Nullable - WorldGlass removeGlassAt(SimpleLocation location); - - /** - * Remove scarecrow data from a certain location - * - * @param location location - */ - @Nullable - WorldScarecrow removeScarecrowAt(SimpleLocation location); - - /** - * Remove any block data from a certain location - * - * @param location location - * @return block data - */ - @Nullable - CustomCropsBlock removeAnythingAt(SimpleLocation location); - - boolean doesChunkHaveScarecrow(SimpleLocation location); - - /** - * If the amount of pot reaches the limitation - * - * @param location location - * @return reach or not - */ - boolean isPotReachLimit(SimpleLocation location); - - /** - * If the amount of crop reaches the limitation - * - * @param location location - * @return reach or not - */ - boolean isCropReachLimit(SimpleLocation location); - - /** - * If the amount of sprinkler reaches the limitation - * - * @param location location - * @return reach or not - */ - boolean isSprinklerReachLimit(SimpleLocation location); - - /** - * Add pot data - * - * @param pot pot data - * @param location location - */ - void addPotAt(WorldPot pot, SimpleLocation location); - - /** - * Add sprinkler data - * - * @param sprinkler sprinkler data - * @param location location - */ - void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location); - - /** - * Add crop data - * - * @param crop crop data - * @param location location - */ - void addCropAt(WorldCrop crop, SimpleLocation location); - - /** - * Add greenhouse glass data - * - * @param glass glass data - * @param location location - */ - void addGlassAt(WorldGlass glass, SimpleLocation location); - - /** - * Add scarecrow data - * - * @param scarecrow scarecrow data - * @param location location - */ - void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java deleted file mode 100644 index 0236184a7..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import com.flowpowered.nbt.Tag; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.SynchronizedCompoundMap; - -public interface DataBlock { - - /** - * Set data by key - * - * @param key key - * @param tag data tag - */ - void setData(String key, Tag tag); - - /** - * Get data tag by key - * - * @param key key - * @return data tag - */ - Tag getData(String key); - - /** - * Get the data map - * - * @return data map - */ - SynchronizedCompoundMap getCompoundMap(); - - /** - * Get the location of the block - * - * @return location - */ - SimpleLocation getLocation(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java deleted file mode 100644 index e56daeee9..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.item.Crop; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; - -public interface WorldCrop extends CustomCropsBlock { - - /** - * Get the key of the crop - * - * @return key - */ - String getKey(); - - /** - * Get the point - * - * @return point - */ - int getPoint(); - - /** - * Set the point - * - * @param point point - */ - void setPoint(int point); - - /** - * Get the config - * - * @return crop config - */ - Crop getConfig(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldGlass.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldGlass.java deleted file mode 100644 index b4b74ce3a..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldGlass.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; - -public interface WorldGlass extends CustomCropsBlock { -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java deleted file mode 100644 index 7cd7a800e..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.item.Pot; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface WorldPot extends CustomCropsBlock { - - /** - * Get the key of the pot - * - * @return key - */ - String getKey(); - - /** - * Get the amount of water - * - * @return amount of water - */ - int getWater(); - - /** - * Set the amount of water - * - * @param water water - */ - void setWater(int water); - - /** - * Get the fertilizer config - * - * @return fertilizer config - */ - @Nullable - Fertilizer getFertilizer(); - - /** - * Set the fertilizer - * - * @param fertilizer fertilizer - */ - void setFertilizer(@NotNull Fertilizer fertilizer); - - /** - * Remove the fertilizer - */ - void removeFertilizer(); - - /** - * Get the remaining usages of the fertilizer - * - * @return remaining usages of the fertilizer - */ - int getFertilizerTimes(); - - /** - * Set the remaining usages of the fertilizer - * - * @param times the remaining usages of the fertilizer - */ - void setFertilizerTimes(int times); - - /** - * Get the pot config - * - * @return pot config - */ - Pot getConfig(); - - /** - * Tick the rainwater and nearby water - */ - void tickWater(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldScarecrow.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldScarecrow.java deleted file mode 100644 index a6725afa3..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldScarecrow.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; - -public interface WorldScarecrow extends CustomCropsBlock { -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java deleted file mode 100644 index 9cab54d01..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.level; - -import net.momirealms.customcrops.api.mechanic.item.Sprinkler; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; - -public interface WorldSprinkler extends CustomCropsBlock { - - /** - * Get the amount of water - * - * @return amount of water - */ - int getWater(); - - /** - * Set the amount of water - * - * @param water amount of water - */ - void setWater(int water); - - /** - * Get the key of the sprinkler - * - * @return key - */ - String getKey(); - - /** - * Get the sprinkler config - * - * @return sprinkler config - */ - Sprinkler getConfig(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/image/WaterBar.java b/api/src/main/java/net/momirealms/customcrops/api/misc/WaterBar.java similarity index 95% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/image/WaterBar.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/WaterBar.java index c1879d6b7..87339d622 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/image/WaterBar.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/WaterBar.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.misc.image; +package net.momirealms.customcrops.api.misc; public record WaterBar(String left, String empty, String full, String right) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/CoolDownManager.java b/api/src/main/java/net/momirealms/customcrops/api/misc/cooldown/CoolDownManager.java similarity index 54% rename from api/src/main/java/net/momirealms/customcrops/api/manager/CoolDownManager.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/cooldown/CoolDownManager.java index 92396f68d..5d2365237 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/manager/CoolDownManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/cooldown/CoolDownManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,30 +15,44 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.manager; +package net.momirealms.customcrops.api.misc.cooldown; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.common.plugin.feature.Reloadable; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import java.util.Collections; import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +/** + * Manages cooldowns for various actions or events. + * Keeps track of cooldown times for different keys associated with player UUIDs. + */ public class CoolDownManager implements Listener, Reloadable { private final ConcurrentHashMap dataMap; - private final CustomCropsPlugin plugin; + private final BukkitCustomCropsPlugin plugin; - public CoolDownManager(CustomCropsPlugin plugin) { + public CoolDownManager(BukkitCustomCropsPlugin plugin) { this.dataMap = new ConcurrentHashMap<>(); this.plugin = plugin; } + /** + * Checks if a player is currently in cooldown for a specific key. + * + * @param uuid The UUID of the player. + * @param key The key associated with the cooldown. + * @param time The cooldown time in milliseconds. + * @return True if the player is in cooldown, false otherwise. + */ public boolean isCoolDown(UUID uuid, String key, long time) { Data data = this.dataMap.computeIfAbsent(uuid, k -> new Data()); return data.isCoolDown(key, time); @@ -46,7 +60,7 @@ public boolean isCoolDown(UUID uuid, String key, long time) { @Override public void load() { - Bukkit.getPluginManager().registerEvents(this, plugin); + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); } @Override @@ -60,6 +74,11 @@ public void disable() { this.dataMap.clear(); } + /** + * Event handler for when a player quits the game. Removes their cooldown data. + * + * @param event The PlayerQuitEvent triggered when a player quits. + */ @EventHandler public void onQuit(PlayerQuitEvent event) { dataMap.remove(event.getPlayer().getUniqueId()); @@ -67,20 +86,27 @@ public void onQuit(PlayerQuitEvent event) { public static class Data { - private final HashMap coolDownMap; + private final Map coolDownMap; public Data() { - this.coolDownMap = new HashMap<>(); + this.coolDownMap = Collections.synchronizedMap(new HashMap<>()); } - public synchronized boolean isCoolDown(String key, long delay) { + /** + * Checks if the player is in cooldown for a specific key. + * + * @param key The key associated with the cooldown. + * @param delay The cooldown delay in milliseconds. + * @return True if the player is in cooldown, false otherwise. + */ + public boolean isCoolDown(String key, long delay) { long time = System.currentTimeMillis(); long last = coolDownMap.getOrDefault(key, time - delay); if (last + delay > time) { - return true; + return true; // Player is in cooldown } else { coolDownMap.put(key, time); - return false; + return false; // Player is not in cooldown } } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/BukkitPlaceholderManager.java b/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/BukkitPlaceholderManager.java new file mode 100644 index 000000000..9fae019b7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/BukkitPlaceholderManager.java @@ -0,0 +1,145 @@ +/* + * 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.misc.placeholder; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.common.util.RandomUtils; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +/** + * This class handles the registration and parsing of custom placeholders, + * and integrates with PlaceholderAPI if available. + */ +public class BukkitPlaceholderManager implements PlaceholderManager { + + private final BukkitCustomCropsPlugin plugin; + private boolean hasPapi; + private final HashMap, String>> customPlaceholderMap; + private static BukkitPlaceholderManager instance; + + /** + * Constructs a new BukkitPlaceholderManager instance. + * + * @param plugin the instance of {@link BukkitCustomCropsPlugin} + */ + public BukkitPlaceholderManager(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + this.customPlaceholderMap = new HashMap<>(); + instance = this; + } + + /** + * Loads the placeholder manager, checking for PlaceholderAPI and registering default placeholders. + */ + @Override + public void load() { + this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); + this.customPlaceholderMap.put("{random}", (p, map) -> String.valueOf(RandomUtils.generateRandomDouble(0, 1))); + } + + /** + * Unloads the placeholder manager, clearing all registered placeholders. + */ + @Override + public void unload() { + this.hasPapi = false; + this.customPlaceholderMap.clear(); + } + + /** + * Gets the singleton instance of BukkitPlaceholderManager. + * + * @return the singleton instance + */ + public static BukkitPlaceholderManager getInstance() { + return instance; + } + + @Override + public boolean registerCustomPlaceholder(String placeholder, String original) { + if (this.customPlaceholderMap.containsKey(placeholder)) return false; + this.customPlaceholderMap.put(placeholder, (p, map) -> PlaceholderAPIUtils.parse(p, parse(p, original, map))); + return true; + } + + @Override + public boolean registerCustomPlaceholder(String placeholder, BiFunction, String> provider) { + if (this.customPlaceholderMap.containsKey(placeholder)) return false; + this.customPlaceholderMap.put(placeholder, provider); + return true; + } + + @Override + public List resolvePlaceholders(String text) { + List placeholders = new ArrayList<>(); + Matcher matcher = PATTERN.matcher(text); + while (matcher.find()) placeholders.add(matcher.group()); + return placeholders; + } + + private String setPlaceholders(OfflinePlayer player, String text) { + return hasPapi ? PlaceholderAPIUtils.parse(player, text) : text; + } + + @Override + public String parseSingle(@Nullable OfflinePlayer player, String placeholder, Map replacements) { + String result = null; + if (replacements != null) + result = replacements.get(placeholder); + if (result != null) + return result; + String custom = Optional.ofNullable(customPlaceholderMap.get(placeholder)).map(supplier -> supplier.apply(player, replacements)).orElse(null); + if (custom == null) + return placeholder; + return setPlaceholders(player, custom); + } + + @Override + public String parse(@Nullable OfflinePlayer player, String text, Map replacements) { + var list = resolvePlaceholders(text); + for (String papi : list) { + String replacer = null; + if (replacements != null) { + replacer = replacements.get(papi); + } + if (replacer == null) { + String custom = Optional.ofNullable(customPlaceholderMap.get(papi)).map(supplier -> supplier.apply(player, replacements)).orElse(null); + if (custom != null) + replacer = setPlaceholders(player, parse(player, custom, replacements)); + } + if (replacer != null) { + text = text.replace(papi, replacer); + } + } + return text; + } + + @Override + public List parse(@Nullable OfflinePlayer player, List list, Map replacements) { + return list.stream() + .map(s -> parse(player, s, replacements)) + .collect(Collectors.toList()); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/PlaceholderAPIUtils.java b/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/PlaceholderAPIUtils.java new file mode 100644 index 000000000..4fdfeaf9a --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/PlaceholderAPIUtils.java @@ -0,0 +1,51 @@ +/* + * 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.misc.placeholder; + +import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +/** + * Utility class for interacting with the PlaceholderAPI. + * Provides methods to parse placeholders in strings for both online and offline players. + */ +public class PlaceholderAPIUtils { + + /** + * Parses placeholders in the provided text for an online player. + * + * @param player The online player for whom the placeholders should be parsed. + * @param text The text containing placeholders to be parsed. + * @return The text with parsed placeholders. + */ + public static String parse(Player player, String text) { + return PlaceholderAPI.setPlaceholders(player, text); + } + + /** + * Parses placeholders in the provided text for an offline player. + * + * @param player The offline player for whom the placeholders should be parsed. + * @param text The text containing placeholders to be parsed. + * @return The text with parsed placeholders. + */ + public static String parse(OfflinePlayer player, String text) { + return PlaceholderAPI.setPlaceholders(player, text); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/PlaceholderManager.java b/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/PlaceholderManager.java new file mode 100644 index 000000000..275d35da2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/placeholder/PlaceholderManager.java @@ -0,0 +1,88 @@ +/* + * 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.misc.placeholder; + +import net.momirealms.customcrops.common.plugin.feature.Reloadable; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.regex.Pattern; + +public interface PlaceholderManager extends Reloadable { + + Pattern PATTERN = Pattern.compile("\\{[^{}]+}"); + + /** + * Registers a custom placeholder. + * + * @param placeholder the placeholder to be registered + * @param original the original placeholder string for instance {@code %test_placeholder%} + * @return true if the placeholder was successfully registered, false otherwise + */ + boolean registerCustomPlaceholder(String placeholder, String original); + + /** + * Registers a custom placeholder. + * + * @param placeholder the placeholder to be registered + * @param provider the value provider + * @return true if the placeholder was successfully registered, false otherwise + */ + boolean registerCustomPlaceholder(String placeholder, BiFunction, String> provider); + + /** + * Resolves all placeholders within a given text. + * + * @param text the text to resolve placeholders in. + * @return a list of found placeholders. + */ + List resolvePlaceholders(String text); + + /** + * Parses a single placeholder for the specified player, using the provided replacements. + * + * @param player the player for whom the placeholder is being parsed + * @param placeholder the placeholder to be parsed + * @param replacements a map of replacements to be used + * @return the parsed placeholder value + */ + String parseSingle(@Nullable OfflinePlayer player, String placeholder, Map replacements); + + /** + * Parses placeholders in the given text for the specified player, using the provided replacements. + * + * @param player the player for whom placeholders are being parsed + * @param text the text containing placeholders + * @param replacements a map of replacements to be used + * @return the text with placeholders replaced + */ + String parse(@Nullable OfflinePlayer player, String text, Map replacements); + + /** + * Parses placeholders in the given list of texts for the specified player, using the provided replacements. + * + * @param player the player for whom placeholders are being parsed + * @param list the list of texts containing placeholders + * @param replacements a map of replacements to be used + * @return the list of texts with placeholders replaced + */ + List parse(@Nullable OfflinePlayer player, List list, Map replacements); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/DynamicText.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/DynamicText.java new file mode 100644 index 000000000..816df3437 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/DynamicText.java @@ -0,0 +1,76 @@ +/* + * 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.misc.value; + +import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class DynamicText { + + private final Player owner; + private String originalValue; + private String latestValue; + private String[] placeholders; + + public DynamicText(Player owner, String rawValue) { + this.owner = owner; + analyze(rawValue); + } + + private void analyze(String value) { + // Analyze the provided text to find and replace placeholders with '%s'. + // Store the original value, placeholders, and the initial latest value. + List placeholdersOwner = new ArrayList<>(BukkitPlaceholderManager.getInstance().resolvePlaceholders(value)); + String origin = value; + for (String placeholder : placeholdersOwner) { + origin = origin.replace(placeholder, "%s"); + } + originalValue = origin; + placeholders = placeholdersOwner.toArray(new String[0]); + latestValue = originalValue; + } + + public String getLatestValue() { + return latestValue; + } + + public boolean update(Map placeholders) { + String string = originalValue; + if (this.placeholders.length != 0) { + BukkitPlaceholderManager bukkitPlaceholderManager = BukkitPlaceholderManager.getInstance(); + if ("%s".equals(originalValue)) { + string = bukkitPlaceholderManager.parseSingle(owner, this.placeholders[0], placeholders); + } else { + Object[] values = new String[this.placeholders.length]; + for (int i = 0; i < this.placeholders.length; i++) { + values[i] = bukkitPlaceholderManager.parseSingle(owner, this.placeholders[i], placeholders); + } + string = String.format(originalValue, values); + } + } + if (!latestValue.equals(string)) { + latestValue = string; + return true; + } + return false; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/ExpressionMathValueImpl.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/ExpressionMathValueImpl.java new file mode 100644 index 000000000..56bb5f094 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/ExpressionMathValueImpl.java @@ -0,0 +1,41 @@ +/* + * 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.misc.value; + + +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.common.helper.ExpressionHelper; + +public class ExpressionMathValueImpl implements MathValue { + + private final TextValue raw; + + public ExpressionMathValueImpl(String raw) { + this.raw = TextValue.auto(raw); + } + + @Override + public double evaluate(Context context) { + return ExpressionHelper.evaluate(raw.render(context)); + } + + @Override + public double evaluate(Context context, boolean parseRawPlaceholders) { + return ExpressionHelper.evaluate(raw.render(context, parseRawPlaceholders)); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/MathValue.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/MathValue.java new file mode 100644 index 000000000..f31b859b8 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/MathValue.java @@ -0,0 +1,123 @@ +/* + * 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.misc.value; + +import net.momirealms.customcrops.api.context.Context; + +/** + * The MathValue interface represents a mathematical value that can be evaluated + * within a specific context. This interface allows for the evaluation of mathematical + * expressions or plain numerical values in the context of custom fishing mechanics. + * + * @param the type of the holder object for the context + */ +public interface MathValue { + + /** + * Evaluates the mathematical value within the given context. + * + * @param context the context in which the value is evaluated + * @return the evaluated value as a double + */ + double evaluate(Context context); + + /** + * Evaluates the mathematical value within the given context. + * + * @param context the context in which the value is evaluated + * @param parseRawPlaceholders whether to parse raw placeholders for instance %xxx% + * @return the evaluated value as a double + */ + default double evaluate(Context context, boolean parseRawPlaceholders) { + return evaluate(context); + } + + /** + * Creates a MathValue based on a mathematical expression. + * + * @param expression the mathematical expression to evaluate + * @param the type of the holder object for the context + * @return a MathValue instance representing the given expression + */ + static MathValue expression(String expression) { + return new ExpressionMathValueImpl<>(expression); + } + + /** + * Creates a MathValue based on a plain numerical value. + * + * @param value the numerical value to represent + * @param the type of the holder object for the context + * @return a MathValue instance representing the given plain value + */ + static MathValue plain(double value) { + return new PlainMathValueImpl<>(value); + } + + /** + * Creates a MathValue based on a range of values. + * + * @param value the ranged value to represent + * @param the type of the holder object for the context + * @return a MathValue instance representing the given ranged value + */ + static MathValue rangedDouble(String value) { + return new RangedDoubleValueImpl<>(value); + } + + /** + * Creates a MathValue based on a range of values. + * + * @param value the ranged value to represent + * @param the type of the holder object for the context + * @return a MathValue instance representing the given ranged value + */ + static MathValue rangedInt(String value) { + return new RangedIntValueImpl<>(value); + } + + /** + * Automatically creates a MathValue based on the given object. + * If the object is a String, it is treated as a mathematical expression. + * If the object is a numerical type (Double, Integer, Long, Float), it is treated as a plain value. + * + * @param o the object to evaluate and create a MathValue from + * @param the type of the holder object for the context + * @return a MathValue instance representing the given object, either as an expression or a plain value + * @throws IllegalArgumentException if the object type is not supported + */ + static MathValue auto(Object o) { + return auto(o, false); + } + + static MathValue auto(Object o, boolean intFirst) { + if (o instanceof String s) { + if (s.contains("~")) { + return intFirst ? (s.contains(".") ? rangedDouble(s) : rangedInt(s)) : rangedDouble(s); + } + try { + return plain(Double.parseDouble(s)); + } catch (NumberFormatException e) { + return expression(s); + } + } else if (o instanceof Number n) { + return plain(n.doubleValue()); + } + throw new IllegalArgumentException("Unsupported type: " + o.getClass()); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/PlaceholderTextValueImpl.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/PlaceholderTextValueImpl.java new file mode 100644 index 000000000..df6fd9716 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/PlaceholderTextValueImpl.java @@ -0,0 +1,42 @@ +/* + * 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.misc.value; + +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; +import org.bukkit.OfflinePlayer; + +import java.util.Map; + +public class PlaceholderTextValueImpl implements TextValue { + + private final String raw; + + public PlaceholderTextValueImpl(String raw) { + this.raw = raw; + } + + @Override + public String render(Context context) { + Map replacements = context.placeholderMap(); + String text; + if (context.holder() instanceof OfflinePlayer player) text = BukkitPlaceholderManager.getInstance().parse(player, raw, replacements); + else text = BukkitPlaceholderManager.getInstance().parse(null, raw, replacements); + return text; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/PlainValue.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/PlainMathValueImpl.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/PlainValue.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/value/PlainMathValueImpl.java index 5a56f70e7..e4885e5a0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/PlainValue.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/PlainMathValueImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,21 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.misc.value; +package net.momirealms.customcrops.api.misc.value; -import net.momirealms.customcrops.api.mechanic.misc.Value; -import org.bukkit.entity.Player; +import net.momirealms.customcrops.api.context.Context; -public class PlainValue implements Value { +public class PlainMathValueImpl implements MathValue { private final double value; - public PlainValue(double value) { + public PlainMathValueImpl(double value) { this.value = value; } @Override - public double get(Player player) { + public double evaluate(Context context) { return value; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/EmptyAction.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/PlainTextValueImpl.java similarity index 64% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/action/EmptyAction.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/value/PlainTextValueImpl.java index 811f8b0ac..28bdafc01 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/EmptyAction.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/PlainTextValueImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,16 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.action; +package net.momirealms.customcrops.api.misc.value; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.context.Context; -public class EmptyAction implements Action { +public class PlainTextValueImpl implements TextValue { - public static EmptyAction instance = new EmptyAction(); + private final String raw; + + public PlainTextValueImpl(String raw) { + this.raw = raw; + } @Override - public void trigger(State state) { + public String render(Context context) { + return raw; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/RangedDoubleValueImpl.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/RangedDoubleValueImpl.java new file mode 100644 index 000000000..142d64933 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/RangedDoubleValueImpl.java @@ -0,0 +1,41 @@ +/* + * 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.misc.value; + +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.common.util.RandomUtils; + +public class RangedDoubleValueImpl implements MathValue { + + private final MathValue min; + private final MathValue max; + + public RangedDoubleValueImpl(String value) { + String[] split = value.split("~"); + if (split.length != 2) { + throw new IllegalArgumentException("Correct ranged format `a~b`"); + } + this.min = MathValue.auto(split[0]); + this.max = MathValue.auto(split[1]); + } + + @Override + public double evaluate(Context context) { + return RandomUtils.generateRandomDouble(min.evaluate(context), max.evaluate(context)); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/RangedIntValueImpl.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/RangedIntValueImpl.java new file mode 100644 index 000000000..7d16b38cd --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/RangedIntValueImpl.java @@ -0,0 +1,41 @@ +/* + * 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.misc.value; + +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.common.util.RandomUtils; + +public class RangedIntValueImpl implements MathValue { + + private final MathValue min; + private final MathValue max; + + public RangedIntValueImpl(String value) { + String[] split = value.split("~"); + if (split.length != 2) { + throw new IllegalArgumentException("Correct ranged format `a~b`"); + } + this.min = MathValue.auto(split[0]); + this.max = MathValue.auto(split[1]); + } + + @Override + public double evaluate(Context context) { + return RandomUtils.generateRandomInt((int) min.evaluate(context), (int) max.evaluate(context)); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/TextValue.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/TextValue.java new file mode 100644 index 000000000..3366bc476 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/TextValue.java @@ -0,0 +1,96 @@ +/* + * 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.misc.value; + +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.misc.placeholder.PlaceholderAPIUtils; +import org.bukkit.OfflinePlayer; + +import java.util.regex.Pattern; + +/** + * The TextValue interface represents a text value that can be rendered + * within a specific context. This interface allows for the rendering of + * placeholder-based or plain text values in the context of custom fishing mechanics. + * + * @param the type of the holder object for the context + */ +public interface TextValue { + + Pattern pattern = Pattern.compile("\\{[^{}]+}"); + + /** + * Renders the text value within the given context. + * + * @param context the context in which the text value is rendered + * @return the rendered text as a String + */ + String render(Context context); + + /** + * Renders the text value within the given context. + * + * @param context the context in which the text value is rendered + * @param parseRawPlaceholders whether to parse raw placeholders for instance %xxx% + * @return the rendered text as a String + */ + default String render(Context context, boolean parseRawPlaceholders) { + if (!parseRawPlaceholders || !(context.holder() instanceof OfflinePlayer player)) return render(context); + return PlaceholderAPIUtils.parse(player, render(context)); + } + + /** + * Creates a TextValue based on a placeholder text. + * Placeholders can be dynamically replaced with context-specific values. + * + * @param text the placeholder text to render + * @param the type of the holder object for the context + * @return a TextValue instance representing the given placeholder text + */ + static TextValue placeholder(String text) { + return new PlaceholderTextValueImpl<>(text); + } + + /** + * Creates a TextValue based on plain text. + * + * @param text the plain text to render + * @param the type of the holder object for the context + * @return a TextValue instance representing the given plain text + */ + static TextValue plain(String text) { + return new PlainTextValueImpl<>(text); + } + + /** + * Automatically creates a TextValue based on the given argument. + * If the argument contains placeholders (detected by a regex pattern), + * a PlaceholderTextValueImpl instance is created. Otherwise, a PlainTextValueImpl + * instance is created. + * + * @param arg the text to evaluate and create a TextValue from + * @param the type of the holder object for the context + * @return a TextValue instance representing the given text, either as a placeholder or plain text + */ + static TextValue auto(String arg) { + if (pattern.matcher(arg).find()) + return placeholder(arg); + else + return plain(arg); + } +} 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 new file mode 100644 index 000000000..4e6b72712 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java @@ -0,0 +1,1118 @@ +package net.momirealms.customcrops.api.requirement; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.block.CrowAttack; +import net.momirealms.customcrops.api.core.block.GreenhouseBlock; +import net.momirealms.customcrops.api.core.block.PotBlock; +import net.momirealms.customcrops.api.core.block.ScarecrowBlock; +import net.momirealms.customcrops.api.core.item.Fertilizer; +import net.momirealms.customcrops.api.core.item.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.misc.value.MathValue; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.util.MoonPhase; +import net.momirealms.customcrops.common.util.ClassUtils; +import net.momirealms.customcrops.common.util.ListUtils; +import net.momirealms.customcrops.common.util.Pair; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.data.type.Farmland; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public abstract class AbstractRequirementManager implements RequirementManager { + + private final HashMap> requirementFactoryMap = new HashMap<>(); + private static final String EXPANSION_FOLDER = "expansions/requirement"; + protected final BukkitCustomCropsPlugin plugin; + protected Class tClass; + + public AbstractRequirementManager(BukkitCustomCropsPlugin plugin, Class tClass) { + this.plugin = plugin; + this.tClass = tClass; + this.registerBuiltInRequirements(); + } + + protected void registerBuiltInRequirements() { + this.registerEnvironmentRequirement(); + this.registerTimeRequirement(); + this.registerYRequirement(); + this.registerAndRequirement(); + this.registerOrRequirement(); + this.registerMoonPhaseRequirement(); + this.registerRandomRequirement(); + this.registerBiomeRequirement(); + this.registerSeasonRequirement(); + this.registerWorldRequirement(); + this.registerDateRequirement(); + this.registerWeatherRequirement(); + this.registerPAPIRequirement(); + this.registerLightRequirement(); + this.registerTemperatureRequirement(); + this.registerFertilizerRequirement(); + this.registerPotRequirement(); + this.registerCrowAttackRequirement(); + this.registerWaterRequirement(); + } + + @Override + public boolean registerRequirement(@NotNull RequirementFactory requirementFactory, @NotNull String... types) { + for (String type : types) { + if (this.requirementFactoryMap.containsKey(type)) return false; + } + for (String type : types) { + this.requirementFactoryMap.put(type, requirementFactory); + } + return true; + } + + @Override + public boolean unregisterRequirement(@NotNull String type) { + return this.requirementFactoryMap.remove(type) != null; + } + + @Override + public boolean hasRequirement(@NotNull String type) { + return requirementFactoryMap.containsKey(type); + } + + @Nullable + @Override + public RequirementFactory getRequirementFactory(@NotNull String type) { + return requirementFactoryMap.get(type); + } + + @NotNull + @SuppressWarnings("unchecked") + @Override + public Requirement[] parseRequirements(Section section, boolean runActions) { + List> requirements = new ArrayList<>(); + if (section != null) + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String typeOrName = entry.getKey(); + if (hasRequirement(typeOrName)) { + requirements.add(parseRequirement(typeOrName, entry.getValue())); + } else { + Section inner = section.getSection(typeOrName); + if (inner != null) { + requirements.add(parseRequirement(inner, runActions)); + } else { + plugin.getPluginLogger().warn("Section " + typeOrName + " is misconfigured"); + } + } + } + return requirements.toArray(new Requirement[0]); + } + + @NotNull + @Override + public Requirement parseRequirement(@NotNull Section section, boolean runActions) { + List> actionList = new ArrayList<>(); + if (runActions && section.contains("not-met-actions")) { + Action[] actions = plugin.getActionManager(tClass).parseActions(requireNonNull(section.getSection("not-met-actions"))); + actionList.addAll(List.of(actions)); + } + String type = section.getString("type"); + if (type == null) { + plugin.getPluginLogger().warn("No requirement type found at " + section.getRouteAsString()); + return Requirement.empty(); + } + var factory = getRequirementFactory(type); + if (factory == null) { + plugin.getPluginLogger().warn("Requirement type: " + type + " not exists"); + return Requirement.empty(); + } + return factory.process(section.get("value"), actionList, runActions); + } + + @NotNull + @Override + public Requirement parseRequirement(@NotNull String type, @NotNull Object value) { + RequirementFactory factory = getRequirementFactory(type); + if (factory == null) { + plugin.getPluginLogger().warn("Requirement type: " + type + " doesn't exist."); + return Requirement.empty(); + } + return factory.process(value); + } + + @SuppressWarnings({"ResultOfMethodCallIgnored", "unchecked"}) + protected void loadExpansions(Class tClass) { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + List>> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class> expansionClass = (Class>) ClassUtils.findClass(expansionJar, RequirementExpansion.class, tClass); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + plugin.getPluginLogger().warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class> expansionClass : classes) { + RequirementExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterRequirement(expansion.getRequirementType()); + registerRequirement(expansion.getRequirementFactory(), expansion.getRequirementType()); + plugin.getPluginLogger().info("Loaded requirement expansion: " + expansion.getRequirementType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor()); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | + NoSuchMethodException e) { + plugin.getPluginLogger().warn("Error occurred when creating expansion instance.", e); + } + } + + protected void registerEnvironmentRequirement() { + registerRequirement((args, actions, advanced) -> { + List environments = ListUtils.toList(args); + return context -> { + Location location = context.arg(ContextKeys.LOCATION); + if (location == null) return false; + var name = location.getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); + if (environments.contains(name)) return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "environment"); + registerRequirement((args, actions, advanced) -> { + List environments = ListUtils.toList(args); + return context -> { + Location location = context.arg(ContextKeys.LOCATION); + if (location == null) return false; + var name = location.getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); + if (!environments.contains(name)) return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "!environment"); + } + + protected void registerTimeRequirement() { + registerRequirement((args, actions, runActions) -> { + List list = ListUtils.toList(args); + List> timePairs = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + }).toList(); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + long time = location.getWorld().getTime(); + for (Pair pair : timePairs) + if (time >= pair.left() && time <= pair.right()) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "time"); + } + + protected void registerYRequirement() { + registerRequirement((args, actions, runActions) -> { + List list = ListUtils.toList(args); + List> posPairs = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Double.parseDouble(split[0]), Double.parseDouble(split[1])); + }).toList(); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + double y = location.getY(); + for (Pair pair : posPairs) + if (y >= pair.left() && y <= pair.right()) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "ypos"); + } + + protected void registerOrRequirement() { + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + Requirement[] requirements = parseRequirements(section, runActions); + return context -> { + for (Requirement requirement : requirements) + if (requirement.isSatisfied(context)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at || requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "||"); + } + + protected void registerAndRequirement() { + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + Requirement[] requirements = parseRequirements(section, runActions); + return context -> { + outer: { + for (Requirement requirement : requirements) + if (!requirement.isSatisfied(context)) + break outer; + return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at && requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "&&"); + } + + protected void registerRandomRequirement() { + registerRequirement((args, actions, runActions) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() < value.evaluate(context, true)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "random"); + } + + protected void registerBiomeRequirement() { + registerRequirement((args, actions, runActions) -> { + HashSet biomes = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(location); + if (biomes.contains(currentBiome)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "biome"); + registerRequirement((args, actions, runActions) -> { + HashSet biomes = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(location); + if (!biomes.contains(currentBiome)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "!biome"); + } + + protected void registerMoonPhaseRequirement() { + registerRequirement((args, actions, runActions) -> { + HashSet moonPhases = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + long days = location.getWorld().getFullTime() / 24_000; + if (moonPhases.contains(MoonPhase.getPhase(days).name().toLowerCase(Locale.ENGLISH))) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "moon-phase"); + registerRequirement((args, actions, runActions) -> { + HashSet moonPhases = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + long days = location.getWorld().getFullTime() / 24_000; + if (!moonPhases.contains(MoonPhase.getPhase(days).name().toLowerCase(Locale.ENGLISH))) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "!moon-phase"); + } + + protected void registerWorldRequirement() { + registerRequirement((args, actions, runActions) -> { + HashSet worlds = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + if (worlds.contains(location.getWorld().getName())) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "world"); + registerRequirement((args, actions, runActions) -> { + HashSet worlds = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + if (!worlds.contains(location.getWorld().getName())) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "!world"); + } + + protected void registerWeatherRequirement() { + registerRequirement((args, actions, runActions) -> { + HashSet weathers = new HashSet<>(ListUtils.toList(args)); + return context -> { + String currentWeather; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + World world = location.getWorld(); + if (world.isClearWeather()) currentWeather = "clear"; + else if (world.isThundering()) currentWeather = "thunder"; + else currentWeather = "rain"; + if (weathers.contains(currentWeather)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "weather"); + } + + protected void registerDateRequirement() { + registerRequirement((args, actions, runActions) -> { + HashSet dates = new HashSet<>(ListUtils.toList(args)); + return context -> { + Calendar calendar = Calendar.getInstance(); + String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE); + if (dates.contains(current)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "date"); + } + + protected void registerSeasonRequirement() { + registerRequirement((args, actions, runActions) -> { + Set seasons = new HashSet<>(ListUtils.toList(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Season season = plugin.getWorldManager().getSeason(location.getWorld()); + if (season == Season.DISABLE) return true; + if (seasons.contains(season.name())) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "season"); + registerRequirement((args, actions, runActions) -> { + Set seasons = new HashSet<>(ListUtils.toList(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Season season = plugin.getWorldManager().getSeason(location.getWorld()); + if (!seasons.contains(season.name())) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "!season"); + registerRequirement((args, actions, runActions) -> { + Set seasons = new HashSet<>(ListUtils.toList(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Season season = plugin.getWorldManager().getSeason(location.getWorld()); + if (season == Season.DISABLE || seasons.contains(season.name())) { + return true; + } + if (ConfigManager.enableGreenhouse()) { + Pos3 pos3 = Pos3.from(location); + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isPresent()) { + CustomCropsWorld cropsWorld = world.get(); + for (int i = 1, range = ConfigManager.greenhouseRange(); i <= range; i++) { + Optional optionalState = cropsWorld.getBlockState(pos3.add(0,i,0)); + if (optionalState.isPresent()) { + if (optionalState.get().type() instanceof GreenhouseBlock) { + return true; + } + } + } + } + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "suitable-season", "suitable_season"); + registerRequirement((args, actions, runActions) -> { + Set seasons = new HashSet<>(ListUtils.toList(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Season season = plugin.getWorldManager().getSeason(location.getWorld()); + if (seasons.contains(season.name())) { + if (ConfigManager.enableGreenhouse()) { + Pos3 pos3 = Pos3.from(location); + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isPresent()) { + CustomCropsWorld cropsWorld = world.get(); + for (int i = 1, range = ConfigManager.greenhouseRange(); i <= range; i++) { + Optional optionalState = cropsWorld.getBlockState(pos3.add(0,i,0)); + if (optionalState.isPresent()) { + if (optionalState.get().type() instanceof GreenhouseBlock) { + if (runActions) ActionManager.trigger(context, actions); + return false; + } + } + } + } + } + return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "unsuitable-season", "unsuitable_season"); + } + + protected void registerPAPIRequirement() { + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context, true) < v2.evaluate(context, true)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at < requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "<"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context, true) <= v2.evaluate(context, true)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at <= requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "<="); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context, true) != v2.evaluate(context, true)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at != requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "!="); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context, true) == v2.evaluate(context, true)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at == requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "=="); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context, true) >= v2.evaluate(context, true)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at >= requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, ">="); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context, true) > v2.evaluate(context, true)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at > requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, ">"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("papi", "")); + String v2 = section.getString("regex", ""); + return context -> { + if (v1.render(context, true).matches(v2)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at regex requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "regex"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (v1.render(context, true).startsWith(v2.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at startsWith requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "startsWith"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (!v1.render(context, true).startsWith(v2.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !startsWith requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "!startsWith"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (v1.render(context, true).endsWith(v2.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at endsWith requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "endsWith"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (!v1.render(context, true).endsWith(v2.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !endsWith requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "!endsWith"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (v1.render(context, true).contains(v2.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at contains requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "contains"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (!v1.render(context, true).contains(v2.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !contains requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "!contains"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue papi = TextValue.auto(section.getString("papi", "")); + List values = ListUtils.toList(section.get("values")); + return context -> { + if (values.contains(papi.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at in-list requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "in-list"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue papi = TextValue.auto(section.getString("papi", "")); + List values = ListUtils.toList(section.get("values")); + return context -> { + if (!values.contains(papi.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !in-list requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "!in-list"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + + return context -> { + if (v1.render(context, true).equals(v2.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at equals requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "equals"); + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (!v1.render(context, true).equals(v2.render(context, true))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !equals requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "!equals"); + } + + protected void registerFertilizerRequirement() { + registerRequirement((args, actions, advanced) -> { + if (args instanceof Section section) { + boolean has = section.getBoolean("has"); + int y = section.getInt("y", 0); + HashSet types = new HashSet<>(ListUtils.toList(section.get("type")).stream().map(str -> str.toUpperCase(Locale.ENGLISH)).toList()); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0,y,0); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return false; + } + Pos3 pos3 = Pos3.from(location); + CustomCropsWorld world = optionalWorld.get(); + Optional optionalState = world.getBlockState(pos3); + if (optionalState.isPresent()) { + if (optionalState.get().type() instanceof PotBlock potBlock) { + Fertilizer[] fertilizers = potBlock.fertilizers(optionalState.get()); + if (fertilizers.length == 0) { + if (!has && types.isEmpty()) { + return true; + } + } else { + if (has) { + if (types.isEmpty()) { + return true; + } + for (Fertilizer fertilizer : fertilizers) { + FertilizerConfig config = fertilizer.config(); + if (config != null) { + if (types.contains(config.type().id())) { + return true; + } + } + } + } else { + outer: { + for (Fertilizer fertilizer : fertilizers) { + FertilizerConfig config = fertilizer.config(); + if (config != null) { + if (types.contains(config.type().id())) { + break outer; + } + } + } + return true; + } + } + } + } + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at fertilizer-type requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "fertilizer_type", "fertilizer-type"); + registerRequirement((args, actions, advanced) -> { + if (args instanceof Section section) { + boolean has = section.getBoolean("has"); + int y = section.getInt("y", 0); + HashSet keys = new HashSet<>(ListUtils.toList(section.get("key"))); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0,y,0); + Pos3 pos3 = Pos3.from(location); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return false; + } + CustomCropsWorld world = optionalWorld.get(); + Optional optionalState = world.getBlockState(pos3); + if (optionalState.isPresent()) { + if (optionalState.get().type() instanceof PotBlock potBlock) { + Fertilizer[] fertilizers = potBlock.fertilizers(optionalState.get()); + if (fertilizers.length == 0) { + if (!has && keys.isEmpty()) { + return true; + } + } else { + if (has) { + if (keys.isEmpty()) { + return true; + } + for (Fertilizer fertilizer : fertilizers) { + if (keys.contains(fertilizer.id())) { + return true; + } + } + } else { + outer: { + for (Fertilizer fertilizer : fertilizers) { + if (keys.contains(fertilizer.id())) { + break outer; + } + } + return true; + } + } + } + } + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at fertilizer requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "fertilizer"); + } + + protected void registerLightRequirement() { + registerRequirement((args, actions, advanced) -> { + List list = ListUtils.toList(args); + List> lightPairs = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + }).toList(); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + int temp = location.getBlock().getLightLevel(); + for (Pair pair : lightPairs) + if (temp >= pair.left() && temp <= pair.right()) + return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "light"); + registerRequirement((args, actions, advanced) -> { + List list = ListUtils.toList(args); + List> lightPairs = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + }).toList(); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + int temp = location.getBlock().getLightFromSky(); + for (Pair pair : lightPairs) + if (temp >= pair.left() && temp <= pair.right()) + return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "natural-light"); + registerRequirement((args, actions, advanced) -> { + int value = (int) args; + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + int light = location.getBlock().getLightFromSky(); + if (light > value) return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "skylight_more_than", "skylight-more-than"); + registerRequirement((args, actions, advanced) -> { + int value = (int) args; + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + int light = location.getBlock().getLightFromSky(); + if (light < value) return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "skylight_less_than", "skylight-less-than"); + registerRequirement((args, actions, advanced) -> { + int value = (int) args; + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + int light = location.getBlock().getLightLevel(); + if (light > value) return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "light_more_than", "light-more-than"); + registerRequirement((args, actions, advanced) -> { + int value = (int) args; + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + int light = location.getBlock().getLightLevel(); + if (light < value) return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "light_less_than", "light-less-than"); + } + + protected void registerTemperatureRequirement() { + registerRequirement((args, actions, advanced) -> { + List list = ListUtils.toList(args); + List> temperaturePairs = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + }).toList(); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + double temp = location.getWorld().getTemperature(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + for (Pair pair : temperaturePairs) + if (temp >= pair.left() && temp <= pair.right()) + return true; + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "temperature"); + } + + private void registerPotRequirement() { + registerRequirement((args, actions, advanced) -> { + if (args instanceof Section section) { + int y = section.getInt("y", 0); + HashSet ids = new HashSet<>(ListUtils.toList(section.get("id"))); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0,y,0); + Pos3 pos3 = Pos3.from(location); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return false; + } + CustomCropsWorld world = optionalWorld.get(); + Optional optionalState = world.getBlockState(pos3); + if (optionalState.isPresent()) { + if (optionalState.get().type() instanceof PotBlock potBlock) { + if (ids.contains(potBlock.id(optionalState.get()))) { + return true; + } + } + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at pot requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "pot"); + } + + private void registerCrowAttackRequirement() { + registerRequirement((args, actions, advanced) -> { + if (args instanceof Section section) { + String flyModel = section.getString("fly-model"); + String standModel = section.getString("stand-model"); + double chance = section.getDouble("chance"); + return (context) -> { + if (Math.random() > chance) return false; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Pos3 pos3 = Pos3.from(location); + if (ConfigManager.enableScarecrow()) { + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isEmpty()) return false; + CustomCropsWorld customCropsWorld = world.get(); + if (!ConfigManager.scarecrowProtectChunk()) { + int range = ConfigManager.scarecrowRange(); + 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; + } + } + } + } + } else { + if (customCropsWorld.doesChunkHaveBlock(pos3, ScarecrowBlock.class)) { + if (advanced) ActionManager.trigger(context, actions); + return false; + } + } + } + if (!Optional.ofNullable(context.arg(ContextKeys.OFFLINE)).orElse(false)) + new CrowAttack( + location, + plugin.getItemManager().build(null, flyModel), + plugin.getItemManager().build(null, standModel) + ).start(); + return true; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at crow-attack requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "crow_attack", "crow-attack"); + } + + protected void registerWaterRequirement() { + registerRequirement((args, actions, advanced) -> { + int value; + int y; + if (args instanceof Integer integer) { + y = -1; + value = integer; + } else if (args instanceof Section section) { + y = section.getInt("y"); + value = section.getInt("value"); + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at water-more-than requirement which is expected be `Section`"); + return Requirement.empty(); + } + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0, y, 0); + Pos3 pos3 = Pos3.from(location); + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isEmpty()) return false; + CustomCropsWorld customCropsWorld = world.get(); + Optional state = customCropsWorld.getBlockState(pos3); + if (state.isPresent() && state.get().type() instanceof PotBlock potBlock) { + int water = potBlock.water(state.get()); + if (water > value) return true; + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "water-more-than", "water_more_than"); + registerRequirement((args, actions, advanced) -> { + int value; + int y; + if (args instanceof Integer integer) { + y = -1; + value = integer; + } else if (args instanceof Section section) { + y = section.getInt("y"); + value = section.getInt("value"); + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at water-less-than requirement which is expected be `Section`"); + return Requirement.empty(); + } + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0, y, 0); + Pos3 pos3 = Pos3.from(location); + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isEmpty()) return false; + CustomCropsWorld customCropsWorld = world.get(); + Optional state = customCropsWorld.getBlockState(pos3); + if (state.isPresent() && state.get().type() instanceof PotBlock potBlock) { + int water = potBlock.water(state.get()); + if (water < value) return true; + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "water-less-than", "water_less_than"); + registerRequirement((args, actions, advanced) -> { + int value; + int y; + if (args instanceof Integer integer) { + y = -1; + value = integer; + } else if (args instanceof Section section) { + y = section.getInt("y"); + value = section.getInt("value"); + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at water-less-than requirement which is expected be `Section`"); + return Requirement.empty(); + } + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0, y, 0); + Block block = location.getBlock(); + if (block.getBlockData() instanceof Farmland farmland) { + return farmland.getMoisture() > value; + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "moisture-more-than", "moisture_more_than"); + registerRequirement((args, actions, advanced) -> { + int value; + int y; + if (args instanceof Integer integer) { + y = -1; + value = integer; + } else if (args instanceof Section section) { + y = section.getInt("y"); + value = section.getInt("value"); + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at water-less-than requirement which is expected be `Section`"); + return Requirement.empty(); + } + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0, y, 0); + Block block = location.getBlock(); + if (block.getBlockData() instanceof Farmland farmland) { + return farmland.getMoisture() < value; + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "moisture-less-than", "moisture_less_than"); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/EmptyRequirement.java b/api/src/main/java/net/momirealms/customcrops/api/requirement/EmptyRequirement.java similarity index 60% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/EmptyRequirement.java rename to api/src/main/java/net/momirealms/customcrops/api/requirement/EmptyRequirement.java index 6d6604155..90323bfde 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/EmptyRequirement.java +++ b/api/src/main/java/net/momirealms/customcrops/api/requirement/EmptyRequirement.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,17 +15,21 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.requirement; +package net.momirealms.customcrops.api.requirement; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.context.Context; -public class EmptyRequirement implements Requirement { +/** + * Represents an empty requirement that always returns true when checking conditions. + */ +public class EmptyRequirement implements Requirement { - public static EmptyRequirement instance = new EmptyRequirement(); + public static EmptyRequirement instance() { + return new EmptyRequirement<>(); + } @Override - public boolean isStateMet(State state) { + public boolean isSatisfied(Context context) { return true; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/requirement/Requirement.java b/api/src/main/java/net/momirealms/customcrops/api/requirement/Requirement.java new file mode 100644 index 000000000..bb9489028 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/requirement/Requirement.java @@ -0,0 +1,41 @@ +/* + * 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.requirement; + +import net.momirealms.customcrops.api.context.Context; + +/** + * Interface representing a requirement that must be met. + * This can be used to define conditions that need to be satisfied within a given context. + * + * @param the type parameter for the context + */ +public interface Requirement { + + /** + * Evaluates whether the requirement is met within the given context. + * + * @param context the context in which the requirement is evaluated + * @return true if the requirement is met, false otherwise + */ + boolean isSatisfied(Context context); + + static Requirement empty() { + return EmptyRequirement.instance(); + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementExpansion.java similarity index 53% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java rename to api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementExpansion.java index 0b70b6ae5..8e84c6b89 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java +++ b/api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementExpansion.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,35 +15,39 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.requirement; +package net.momirealms.customcrops.api.requirement; -public abstract class RequirementExpansion { +/** + * An abstract class representing a requirement expansion + * Requirement expansions are used to define custom requirements for various functionalities. + */ +public abstract class RequirementExpansion { /** - * Get the version number + * Get the version of this requirement expansion. * - * @return version + * @return The version of the expansion. */ public abstract String getVersion(); /** - * Get the author + * Get the author of this requirement expansion. * - * @return author + * @return The author of the expansion. */ public abstract String getAuthor(); /** - * Get the type of the requirement + * Get the type of requirement provided by this expansion. * - * @return the type of the requirement + * @return The type of requirement. */ public abstract String getRequirementType(); /** - * Get the requirement factory + * Get the factory for creating requirements defined by this expansion. * - * @return the requirement factory + * @return The requirement factory. */ - public abstract RequirementFactory getRequirementFactory(); + public abstract RequirementFactory getRequirementFactory(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementFactory.java b/api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementFactory.java new file mode 100644 index 000000000..905962312 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementFactory.java @@ -0,0 +1,50 @@ +/* + * 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.requirement; + +import net.momirealms.customcrops.api.action.Action; + +import java.util.List; + +/** + * Interface representing a factory for creating requirements. + * + * @param the type of object that the requirement will operate on + */ +public interface RequirementFactory { + + /** + * Build a requirement with the given arguments, not satisfied actions, and check run actions flag. + * + * @param args The arguments used to build the requirement. + * @param notSatisfiedActions Actions to be triggered when the requirement is not met (can be null). + * @param runActions Flag indicating whether to run the action if the requirement is not met. + * @return The built requirement. + */ + Requirement process(Object args, List> notSatisfiedActions, boolean runActions); + + /** + * Build a requirement with the given arguments. + * + * @param args The arguments used to build the requirement. + * @return The built requirement. + */ + default Requirement process(Object args) { + return process(args, List.of(), false); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementManager.java b/api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementManager.java new file mode 100644 index 000000000..9bd8e4386 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/requirement/RequirementManager.java @@ -0,0 +1,133 @@ +/* + * 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.requirement; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * The RequirementManager interface manages custom requirement types and provides methods for handling requirements. + * + * @param the type of the context in which the requirements are evaluated. + */ +public interface RequirementManager extends Reloadable { + + /** + * Registers a custom requirement type with its corresponding factory. + * + * @param requirementFactory The factory responsible for creating instances of the requirement. + * @param alias The type identifier of the requirement. + * @return True if registration was successful, false if the type is already registered. + */ + boolean registerRequirement(@NotNull RequirementFactory requirementFactory, @NotNull String... alias); + + /** + * Unregisters a custom requirement type. + * + * @param type The type identifier of the requirement to unregister. + * @return True if unregistration was successful, false if the type is not registered. + */ + boolean unregisterRequirement(@NotNull String type); + + /** + * Checks if a requirement type is registered. + * + * @param type The type identifier of the requirement. + * @return True if the requirement type is registered, otherwise false. + */ + boolean hasRequirement(@NotNull String type); + + /** + * Retrieves a RequirementFactory based on the specified requirement type. + * + * @param type The requirement type for which to retrieve a factory. + * @return A RequirementFactory for the specified type, or null if no factory is found. + */ + @Nullable + RequirementFactory getRequirementFactory(@NotNull String type); + + /** + * Retrieves an array of requirements based on a configuration section. + * + * @param section The configuration section containing requirement definitions. + * @param runActions A flag indicating whether to use advanced requirements. + * @return An array of Requirement objects based on the configuration section. + */ + @NotNull + Requirement[] parseRequirements(Section section, boolean runActions); + + /** + * Retrieves a Requirement object based on a configuration section and advanced flag. + * + * @param section The configuration section containing requirement definitions. + * @param runActions A flag indicating whether to use advanced requirements. + * @return A Requirement object based on the configuration section, or an EmptyRequirement if the section is null or invalid. + */ + @NotNull + Requirement parseRequirement(@NotNull Section section, boolean runActions); + + /** + * Gets a requirement based on the provided type and value. + * If a valid RequirementFactory is found for the type, it is used to create the requirement. + * + * @param type The type representing the requirement type. + * @param value The value associated with the requirement. + * @return A Requirement instance based on the type and value, or an EmptyRequirement if the type is invalid. + */ + @NotNull + Requirement parseRequirement(@NotNull String type, @NotNull Object value); + + /** + * Checks if all requirements in the provided array are satisfied within the given context. + * + * @param context The context in which the requirements are evaluated. + * @param requirements An array of requirements to check. + * @return True if all requirements are satisfied, otherwise false. + */ + static boolean isSatisfied(Context context, @Nullable Requirement[] requirements) { + if (requirements == null) return true; + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied(context)) { + return false; + } + } + return true; + } + + /** + * Checks if all requirements in the provided array are satisfied within the given context. + * + * @param context The context in which the requirements are evaluated. + * @param requirements A list of requirements to check. + * @return True if all requirements are satisfied, otherwise false. + */ + static boolean isSatisfied(Context context, @Nullable List> requirements) { + if (requirements == null) return true; + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied(context)) { + return false; + } + } + return true; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/scheduler/CancellableTask.java b/api/src/main/java/net/momirealms/customcrops/api/scheduler/CancellableTask.java deleted file mode 100644 index d96887857..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/scheduler/CancellableTask.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.scheduler; - -public interface CancellableTask { - - /** - * Cancel the task - */ - void cancel(); - - /** - * Get if the task is cancelled or not - * - * @return cancelled or not - */ - boolean isCancelled(); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/scheduler/Scheduler.java b/api/src/main/java/net/momirealms/customcrops/api/scheduler/Scheduler.java deleted file mode 100644 index 01ba2f470..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/scheduler/Scheduler.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.scheduler; - -import org.bukkit.Location; -import org.bukkit.World; - -import java.util.concurrent.TimeUnit; - -public interface Scheduler { - - /** - * Runs a task synchronously on the main server thread or region thread. - * - * @param runnable The task to run. - * @param location The location associated with the task. - */ - void runTaskSync(Runnable runnable, Location location); - - /** - * Runs a task synchronously on the main server thread or region thread. - * - * @param runnable The task to run. - * @param world world - * @param x chunk X - * @param z chunk Z - */ - void runTaskSync(Runnable runnable, World world, int x, int z); - - /** - * Runs a task synchronously with a specified delay and period. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delayTicks The delay in ticks before the first execution. - * @param periodTicks The period between subsequent executions in ticks. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks); - - /** - * Runs a task asynchronously with a specified delay. - * - * @param runnable The task to run. - * @param delay The delay before the task execution. - * @param timeUnit The time unit for the delay. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit); - - /** - * Runs a task asynchronously. - * - * @param runnable The task to run. - */ - void runTaskAsync(Runnable runnable); - - /** - * Runs a task synchronously with a specified delay. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delay The delay before the task execution. - * @param timeUnit The time unit for the delay. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit timeUnit); - - /** - * Runs a task synchronously with a specified delay in ticks. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delayTicks The delay in ticks before the task execution. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks); - - /** - * Runs a task asynchronously with a specified delay and period. - * - * @param runnable The task to run. - * @param delay The delay before the first execution. - * @param period The period between subsequent executions. - * @param timeUnit The time unit for the delay and period. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/DisplayEntityUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/DisplayEntityUtils.java deleted file mode 100644 index fcef9608c..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/util/DisplayEntityUtils.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.momirealms.customcrops.api.util; - -import net.momirealms.customcrops.api.mechanic.misc.CRotation; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ItemDisplay; - -public class DisplayEntityUtils { - - public static CRotation getRotation(Entity entity) { - if (entity instanceof ItemDisplay itemDisplay) { - return CRotation.getByYaw(itemDisplay.getLocation().getYaw()); - } - return CRotation.NONE; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/EventUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/EventUtils.java index 038de4d5a..723a175fc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/util/EventUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/EventUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -21,12 +21,28 @@ import org.bukkit.event.Cancellable; import org.bukkit.event.Event; +/** + * Utility class for handling events. + */ public class EventUtils { + /** + * Fires an event and does not wait for any result. + * + * @param event the {@link Event} to be fired + */ public static void fireAndForget(Event event) { Bukkit.getPluginManager().callEvent(event); } + /** + * Fires an event and checks if it is cancelled. + * This method only accepts events that implement {@link Cancellable}. + * + * @param event the {@link Event} to be fired + * @return true if the event is cancelled, false otherwise + * @throws IllegalArgumentException if the event is not cancellable + */ public static boolean fireAndCheckCancel(Event event) { if (!(event instanceof Cancellable cancellable)) throw new IllegalArgumentException("Only cancellable events are allowed here"); diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/FakeCancellable.java b/api/src/main/java/net/momirealms/customcrops/api/util/FakeCancellable.java new file mode 100644 index 000000000..3202bc7fb --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/util/FakeCancellable.java @@ -0,0 +1,18 @@ +package net.momirealms.customcrops.api.util; + +import org.bukkit.event.Cancellable; + +public class FakeCancellable implements Cancellable { + + private boolean cancelled; + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/InventoryUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/InventoryUtils.java new file mode 100644 index 000000000..1e8f2eb6f --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/util/InventoryUtils.java @@ -0,0 +1,108 @@ +/* + * 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.util; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Utility class for working with Bukkit Inventories and item stacks. + */ +public class InventoryUtils { + + private InventoryUtils() { + } + + /** + * Serialize an array of ItemStacks to a Base64-encoded string. + * + * @param contents The ItemStack array to serialize. + * @return The Base64-encoded string representing the serialized ItemStacks. + */ + public static @NotNull String stacksToBase64(ItemStack[] contents) { + if (contents == null || contents.length == 0) { + return ""; + } + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); + dataOutput.writeInt(contents.length); + for (ItemStack itemStack : contents) { + dataOutput.writeObject(itemStack); + } + dataOutput.close(); + byte[] byteArr = outputStream.toByteArray(); + outputStream.close(); + return Base64Coder.encodeLines(byteArr); + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + /** + * Deserialize an ItemStack array from a Base64-encoded string. + * + * @param base64 The Base64-encoded string representing the serialized ItemStacks. + * @return An array of ItemStacks deserialized from the input string. + */ + @Nullable + public static ItemStack[] getInventoryItems(String base64) { + ItemStack[] itemStacks = null; + if (base64 == null || base64.isEmpty()) return new ItemStack[]{}; + ByteArrayInputStream inputStream; + try { + inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(base64)); + } catch (IllegalArgumentException ignored) { + return new ItemStack[]{}; + } + BukkitObjectInputStream dataInput = null; + try { + dataInput = new BukkitObjectInputStream(inputStream); + itemStacks = new ItemStack[dataInput.readInt()]; + } catch (IOException ioException) { + ioException.printStackTrace(); + } + if (itemStacks == null) return new ItemStack[]{}; + for (int i = 0; i < itemStacks.length; i++) { + try { + itemStacks[i] = (ItemStack) dataInput.readObject(); + } catch (IOException | ClassNotFoundException | NullPointerException e) { + try { + dataInput.close(); + } catch (IOException ioException) { + ioException.printStackTrace(); + } + return null; + } + } + try { + dataInput.close(); + } catch (IOException ignored) { + } + return itemStacks; + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java index 41c287929..158029a5c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -53,11 +53,20 @@ public static Location toBlockLocation(Location location) { } @NotNull - public static Location toCenterLocation(Location location) { + public static Location toBlockCenterLocation(Location location) { Location centerLoc = location.clone(); centerLoc.setX(location.getBlockX() + 0.5); centerLoc.setY(location.getBlockY() + 0.5); centerLoc.setZ(location.getBlockZ() + 0.5); return centerLoc; } + + @NotNull + public static Location toSurfaceCenterLocation(Location location) { + Location centerLoc = location.clone(); + centerLoc.setX(location.getBlockX() + 0.5); + centerLoc.setZ(location.getBlockZ() + 0.5); + centerLoc.setY(location.getBlockY()); + return centerLoc; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/LogUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/LogUtils.java deleted file mode 100644 index 1f03d49b6..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/util/LogUtils.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.util; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import org.jetbrains.annotations.NotNull; - -import java.util.logging.Level; - -/** - * Utility class for logging messages with various log levels. - */ -public final class LogUtils { - - private LogUtils() {} - - /** - * Log an informational message. - * - * @param message The message to log. - */ - public static void info(@NotNull String message) { - CustomCropsPlugin.getInstance().getLogger().info(message); - } - - /** - * Log a warning message. - * - * @param message The message to log. - */ - public static void warn(@NotNull String message) { - CustomCropsPlugin.getInstance().getLogger().warning(message); - } - - /** - * Log a severe error message. - * - * @param message The message to log. - */ - public static void severe(@NotNull String message) { - CustomCropsPlugin.getInstance().getLogger().severe(message); - } - - /** - * Log a warning message with a throwable exception. - * - * @param message The message to log. - * @param throwable The throwable exception to log. - */ - public static void warn(@NotNull String message, Throwable throwable) { - CustomCropsPlugin.getInstance().getLogger().log(Level.WARNING, message, throwable); - } - - /** - * Log a severe error message with a throwable exception. - * - * @param message The message to log. - * @param throwable The throwable exception to log. - */ - public static void severe(@NotNull String message, Throwable throwable) { - CustomCropsPlugin.getInstance().getLogger().log(Level.SEVERE, message, throwable); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakFurnitureWrapper.java b/api/src/main/java/net/momirealms/customcrops/api/util/MoonPhase.java similarity index 51% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakFurnitureWrapper.java rename to api/src/main/java/net/momirealms/customcrops/api/util/MoonPhase.java index c705fca6c..b0d07b020 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakFurnitureWrapper.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/MoonPhase.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,30 +15,41 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.item.function.wrapper; +package net.momirealms.customcrops.api.util; -import org.bukkit.Location; -import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -public class BreakFurnitureWrapper extends BreakWrapper { +import java.util.HashMap; +import java.util.Map; - private final Location location; - private final String id; +public enum MoonPhase { - public BreakFurnitureWrapper(Player player, Location location, String id) { - super(player, location); - this.location = location; - this.id = id; + FULL_MOON(0L), + WANING_GIBBOUS(1L), + LAST_QUARTER(2L), + WANING_CRESCENT(3L), + NEW_MOON(4L), + WAXING_CRESCENT(5L), + FIRST_QUARTER(6L), + WAXING_GIBBOUS(7L); + + private final long day; + + MoonPhase(long day) { + this.day = day; } - @NotNull - public Location getLocation() { - return location; + private static final Map BY_DAY = new HashMap<>(); + + static { + for (MoonPhase phase : values()) { + BY_DAY.put(phase.day, phase); + } } @NotNull - public String getID() { - return id; + public static MoonPhase getPhase(long day) { + return BY_DAY.get(day % 8L); } } + diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ParticleUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/ParticleUtils.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/util/ParticleUtils.java rename to api/src/main/java/net/momirealms/customcrops/api/util/ParticleUtils.java index 5162f5b73..8481ca6bf 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ParticleUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/ParticleUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.util; +package net.momirealms.customcrops.api.util; import org.bukkit.Particle; diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/PlayerUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/PlayerUtils.java new file mode 100644 index 000000000..8a4b32d91 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/util/PlayerUtils.java @@ -0,0 +1,158 @@ +/* + * 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.util; + +import net.momirealms.customcrops.common.util.RandomUtils; +import org.bukkit.Location; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +public class PlayerUtils { + + public static void dropItem(@NotNull Player player, @NotNull ItemStack itemStack, boolean retainOwnership, boolean noPickUpDelay, boolean throwRandomly) { + requireNonNull(player, "player"); + requireNonNull(itemStack, "itemStack"); + Location location = player.getLocation().clone(); + Item item = player.getWorld().dropItem(player.getEyeLocation().clone().subtract(new Vector(0,0.3,0)), itemStack); + item.setPickupDelay(noPickUpDelay ? 0 : 40); + item.setOwner(player.getUniqueId()); + if (retainOwnership) { + item.setThrower(player.getUniqueId()); + } + if (throwRandomly) { + double d1 = RandomUtils.generateRandomDouble(0,1) * 0.5f; + double d2 = RandomUtils.generateRandomDouble(0,1) * (Math.PI * 2); + item.setVelocity(new Vector(-Math.sin(d2) * d1, 0.2f, Math.cos(d2) * d1)); + } else { + double d1 = Math.sin(location.getPitch() * (Math.PI/180)); + double d2 = RandomUtils.generateRandomDouble(0, 0.02); + double d3 = RandomUtils.generateRandomDouble(0,1) * (Math.PI * 2); + Vector vector = location.getDirection().multiply(0.3).setY(-d1 * 0.3 + 0.1 + (RandomUtils.generateRandomDouble(0,1) - RandomUtils.generateRandomDouble(0,1)) * 0.1); + vector.add(new Vector(Math.cos(d3) * d2, 0, Math.sin(d3) * d2)); + item.setVelocity(vector); + } + } + + public static int putItemsToInventory(Inventory inventory, ItemStack itemStack, int amount) { + ItemMeta meta = itemStack.getItemMeta(); + int maxStackSize = itemStack.getMaxStackSize(); + for (ItemStack other : inventory.getStorageContents()) { + if (other != null) { + if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { + if (other.getAmount() < maxStackSize) { + int delta = maxStackSize - other.getAmount(); + if (amount > delta) { + other.setAmount(maxStackSize); + amount -= delta; + } else { + other.setAmount(amount + other.getAmount()); + return 0; + } + } + } + } + } + + if (amount > 0) { + for (ItemStack other : inventory.getStorageContents()) { + if (other == null) { + if (amount > maxStackSize) { + amount -= maxStackSize; + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + inventory.addItem(cloned); + } else { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(amount); + inventory.addItem(cloned); + return 0; + } + } + } + } + + return amount; + } + + public static int giveItem(Player player, ItemStack itemStack, int amount) { + PlayerInventory inventory = player.getInventory(); + ItemMeta meta = itemStack.getItemMeta(); + int maxStackSize = itemStack.getMaxStackSize(); + if (amount > maxStackSize * 100) { + amount = maxStackSize * 100; + } + int actualAmount = amount; + for (ItemStack other : inventory.getStorageContents()) { + if (other != null) { + if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { + if (other.getAmount() < maxStackSize) { + int delta = maxStackSize - other.getAmount(); + if (amount > delta) { + other.setAmount(maxStackSize); + amount -= delta; + } else { + other.setAmount(amount + other.getAmount()); + return actualAmount; + } + } + } + } + } + if (amount > 0) { + for (ItemStack other : inventory.getStorageContents()) { + if (other == null) { + if (amount > maxStackSize) { + amount -= maxStackSize; + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + inventory.addItem(cloned); + } else { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(amount); + inventory.addItem(cloned); + return actualAmount; + } + } + } + } + + if (amount > 0) { + for (int i = 0; i < amount / maxStackSize; i++) { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + player.getWorld().dropItem(player.getLocation(), cloned); + } + int left = amount % maxStackSize; + if (left != 0) { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(left); + player.getWorld().dropItem(player.getLocation(), cloned); + } + } + + return actualAmount; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/PluginUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/PluginUtils.java new file mode 100644 index 000000000..982e7ca78 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/util/PluginUtils.java @@ -0,0 +1,20 @@ +package net.momirealms.customcrops.api.util; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +public class PluginUtils { + + public static boolean isEnabled(String name) { + return Bukkit.getPluginManager().isPluginEnabled(name); + } + + @SuppressWarnings("deprecation") + public static String getPluginVersion(String name) { + Plugin plugin = Bukkit.getPluginManager().getPlugin(name); + if (plugin != null) { + return plugin.getDescription().getVersion(); + } + return ""; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java index 65e1687bc..860ac8f72 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java @@ -12,4 +12,14 @@ public static boolean isCapitalLetter(String item) { return true; } + public static String toLowerCase(String input) { + char[] chars = input.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + if (c >= 'A' && c <= 'Z') { + chars[i] = (char) (c + 32); + } + } + return new String(chars); + } } diff --git a/build.gradle.kts b/build.gradle.kts index 1c3a24125..d72e8d7f6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,81 +1,49 @@ +import java.io.ByteArrayOutputStream + plugins { - id("org.gradle.java") - id("application") - id("org.gradle.maven-publish") - id("io.github.goooler.shadow") version "8.1.7" + id("java") } -allprojects { +val git : String = versionBanner() +val builder : String = builder() +ext["git_version"] = git +ext["builder"] = builder - project.group = "net.momirealms" - project.version = "3.5.11" +subprojects { - apply() apply(plugin = "java") - apply(plugin = "application") - apply(plugin = "io.github.goooler.shadow") - apply(plugin = "org.gradle.maven-publish") - - application { - mainClass.set("") - } + apply(plugin = "java-library") - repositories { - mavenCentral() - maven("https://maven.aliyun.com/repository/public/") - maven("https://betonquest.org/nexus/repository/betonquest/") - maven("https://maven.enginehub.org/repo/") - maven("https://oss.sonatype.org/content/groups/public/") - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") - maven("https://repo.codemc.org/repository/maven-public/") - maven("https://jitpack.io") - maven("https://repo.papermc.io/repository/maven-public/") - maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") - maven("https://repo.dmulloy2.net/repository/public/") - maven("https://mvn.lumine.io/repository/maven-public/") - maven("https://repo.bg-software.com/repository/api/") - maven("https://repo.infernalsuite.com/repository/maven-snapshots/") - maven("https://repo.rapture.pw/repository/maven-releases/") - maven("https://nexus.phoenixdevt.fr/repository/maven-public/") - maven("https://r.irepo.space/maven/") - maven("https://repo.auxilor.io/repository/maven-public/") - maven("https://nexus.betonquest.org/repository/betonquest/") - maven("https://repo.infernalsuite.com/repository/maven-releases/") - maven("https://repo.rapture.pw/repository/maven-releases/") - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") - maven("https://repo.xenondevs.xyz/releases/") - maven("https://repo.oraxen.com/snapshots/") - } -} - -subprojects { tasks.processResources { - val props = mapOf("version" to version) - inputs.properties(props) filteringCharset = "UTF-8" - filesMatching("*plugin.yml") { - expand(props) + + filesMatching(arrayListOf("custom-crops.properties")) { + expand(rootProject.properties) } - } - tasks.shadowJar { - if (arrayListOf("plugin", "api").contains(project.name)) { - destinationDirectory.set(file("$rootDir/target")) + filesMatching(arrayListOf("*.yml", "*/*.yml")) { + expand( + Pair("project_version", rootProject.properties["project_version"]), + Pair("config_version", rootProject.properties["config_version"]) + ) } - archiveClassifier.set("") - archiveFileName.set("CustomCrops-" + project.name + "-" + project.version + ".jar") } +} - if ("api" == project.name) { - publishing { - publications { - create("mavenJava") { - groupId = "net.momirealms" - artifactId = "CustomCrops" - version = rootProject.version.toString() - artifact(tasks.shadowJar) - } - } - } +fun versionBanner(): String { + val os = ByteArrayOutputStream() + project.exec { + commandLine = "git rev-parse --short=8 HEAD".split(" ") + standardOutput = os + } + return String(os.toByteArray()).trim() +} + +fun builder(): String { + val os = ByteArrayOutputStream() + project.exec { + commandLine = "git config user.name".split(" ") + standardOutput = os } + return String(os.toByteArray()).trim() } \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 000000000..cd6333bfb --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,39 @@ +repositories { + mavenCentral() + maven("https://jitpack.io/") // rtag +} + +dependencies { + compileOnly("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") { + exclude(module = "adventure-bom") + exclude(module = "checker-qual") + exclude(module = "annotations") + } + compileOnly("org.incendo:cloud-core:${rootProject.properties["cloud_core_version"]}") + compileOnly("org.incendo:cloud-minecraft-extras:${rootProject.properties["cloud_minecraft_extras_version"]}") + compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + compileOnly("org.jetbrains:annotations:${rootProject.properties["jetbrains_annotations_version"]}") + compileOnly("org.slf4j:slf4j-api:${rootProject.properties["slf4j_version"]}") + compileOnly("org.apache.logging.log4j:log4j-core:${rootProject.properties["log4j_version"]}") + compileOnly("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}") + compileOnly("com.github.ben-manes.caffeine:caffeine:${rootProject.properties["caffeine_version"]}") + compileOnly("com.saicone.rtag:rtag:${rootProject.properties["rtag_version"]}") + compileOnly("net.objecthunter:exp4j:${rootProject.properties["exp4j_version"]}") + compileOnly("com.google.guava:guava:${rootProject.properties["guava_version"]}") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customcrops/common/annotation/DoNotUse.java b/common/src/main/java/net/momirealms/customcrops/common/annotation/DoNotUse.java new file mode 100644 index 000000000..ef22d08d7 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/annotation/DoNotUse.java @@ -0,0 +1,11 @@ +package net.momirealms.customcrops.common.annotation; + +import org.jetbrains.annotations.ApiStatus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@ApiStatus.Internal +@Target({ElementType.FIELD}) +public @interface DoNotUse { +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/command/AbstractCommandFeature.java b/common/src/main/java/net/momirealms/customcrops/common/command/AbstractCommandFeature.java new file mode 100644 index 000000000..e2ab75e5c --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/command/AbstractCommandFeature.java @@ -0,0 +1,85 @@ +/* + * 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.common.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customcrops.common.sender.SenderFactory; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; + +public abstract class AbstractCommandFeature implements CommandFeature { + + protected final CustomCropsCommandManager commandManager; + protected CommandConfig commandConfig; + + public AbstractCommandFeature(CustomCropsCommandManager commandManager) { + this.commandManager = commandManager; + } + + protected abstract SenderFactory getSenderFactory(); + + public abstract Command.Builder assembleCommand(CommandManager manager, Command.Builder builder); + + @Override + @SuppressWarnings("unchecked") + public Command registerCommand(CommandManager manager, Command.Builder builder) { + Command command = (Command) assembleCommand(manager, builder).build(); + manager.command(command); + return command; + } + + @Override + public void registerRelatedFunctions() { + // empty + } + + @Override + public void unregisterRelatedFunctions() { + // empty + } + + @Override + @SuppressWarnings("unchecked") + public void handleFeedback(CommandContext context, TranslatableComponent.Builder key, Component... args) { + if (context.flags().hasFlag("silent")) { + return; + } + commandManager.handleCommandFeedback((C) context.sender(), key, args); + } + + @Override + public void handleFeedback(C sender, TranslatableComponent.Builder key, Component... args) { + commandManager.handleCommandFeedback(sender, key, args); + } + + @Override + public CustomCropsCommandManager getCustomCropsCommandManager() { + return commandManager; + } + + @Override + public CommandConfig getCommandConfig() { + return commandConfig; + } + + public void setCommandConfig(CommandConfig commandConfig) { + this.commandConfig = commandConfig; + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/command/AbstractCommandManager.java b/common/src/main/java/net/momirealms/customcrops/common/command/AbstractCommandManager.java new file mode 100644 index 000000000..03327e56b --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/command/AbstractCommandManager.java @@ -0,0 +1,179 @@ +/* + * 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.common.command; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customcrops.common.locale.CustomCropsCaptionFormatter; +import net.momirealms.customcrops.common.locale.CustomCropsCaptionProvider; +import net.momirealms.customcrops.common.locale.TranslationManager; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; +import net.momirealms.customcrops.common.sender.Sender; +import net.momirealms.customcrops.common.util.ArrayUtils; +import net.momirealms.customcrops.common.util.TriConsumer; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.caption.Caption; +import org.incendo.cloud.caption.StandardCaptionKeys; +import org.incendo.cloud.component.CommandComponent; +import org.incendo.cloud.exception.*; +import org.incendo.cloud.exception.handling.ExceptionContext; +import org.incendo.cloud.minecraft.extras.MinecraftExceptionHandler; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +public abstract class AbstractCommandManager implements CustomCropsCommandManager { + + protected final HashSet> registeredRootCommandComponents = new HashSet<>(); + protected final HashSet> registeredFeatures = new HashSet<>(); + protected final CommandManager commandManager; + protected final CustomCropsPlugin plugin; + private final CustomCropsCaptionFormatter captionFormatter = new CustomCropsCaptionFormatter(); + private final MinecraftExceptionHandler.Decorator decorator = (formatter, ctx, msg) -> msg; + + private TriConsumer feedbackConsumer; + + public AbstractCommandManager(CustomCropsPlugin plugin, CommandManager commandManager) { + this.commandManager = commandManager; + this.plugin = plugin; + this.inject(); + this.feedbackConsumer = defaultFeedbackConsumer(); + } + + @Override + public void setFeedbackConsumer(@NotNull TriConsumer feedbackConsumer) { + this.feedbackConsumer = feedbackConsumer; + } + + @Override + public TriConsumer defaultFeedbackConsumer() { + return ((sender, node, component) -> { + wrapSender(sender).sendMessage( + component, true + ); + }); + } + + protected abstract Sender wrapSender(C c); + + private void inject() { + getCommandManager().captionRegistry().registerProvider(new CustomCropsCaptionProvider<>()); + injectExceptionHandler(InvalidSyntaxException.class, MinecraftExceptionHandler.createDefaultInvalidSyntaxHandler(), StandardCaptionKeys.EXCEPTION_INVALID_SYNTAX); + injectExceptionHandler(InvalidCommandSenderException.class, MinecraftExceptionHandler.createDefaultInvalidSenderHandler(), StandardCaptionKeys.EXCEPTION_INVALID_SENDER); + injectExceptionHandler(NoPermissionException.class, MinecraftExceptionHandler.createDefaultNoPermissionHandler(), StandardCaptionKeys.EXCEPTION_NO_PERMISSION); + injectExceptionHandler(ArgumentParseException.class, MinecraftExceptionHandler.createDefaultArgumentParsingHandler(), StandardCaptionKeys.EXCEPTION_INVALID_ARGUMENT); + injectExceptionHandler(CommandExecutionException.class, MinecraftExceptionHandler.createDefaultCommandExecutionHandler(), StandardCaptionKeys.EXCEPTION_UNEXPECTED); + } + + private void injectExceptionHandler(Class type, MinecraftExceptionHandler.MessageFactory factory, Caption key) { + getCommandManager().exceptionController().registerHandler(type, ctx -> { + final @Nullable ComponentLike message = factory.message(captionFormatter, (ExceptionContext) ctx); + if (message != null) { + handleCommandFeedback(ctx.context().sender(), key.key(), decorator.decorate(captionFormatter, ctx, message.asComponent()).asComponent()); + } + }); + } + + @Override + public CommandConfig getCommandConfig(YamlDocument document, String featureID) { + Section section = document.getSection(featureID); + if (section == null) return null; + return new CommandConfig.Builder() + .permission(section.getString("permission")) + .usages(section.getStringList("usage")) + .enable(section.getBoolean("enable", false)) + .build(); + } + + @Override + public Collection> buildCommandBuilders(CommandConfig config) { + ArrayList> list = new ArrayList<>(); + for (String usage : config.getUsages()) { + if (!usage.startsWith("/")) continue; + String command = usage.substring(1).trim(); + String[] split = command.split(" "); + Command.Builder builder = new CommandBuilder.BasicCommandBuilder<>(getCommandManager(), split[0]) + .setCommandNode(ArrayUtils.subArray(split, 1)) + .setPermission(config.getPermission()) + .getBuiltCommandBuilder(); + list.add(builder); + } + return list; + } + + @Override + public void registerFeature(CommandFeature feature, CommandConfig config) { + if (!config.isEnable()) throw new RuntimeException("Registering a disabled command feature is not allowed"); + for (Command.Builder builder : buildCommandBuilders(config)) { + Command command = feature.registerCommand(commandManager, builder); + this.registeredRootCommandComponents.add(command.rootComponent()); + } + feature.registerRelatedFunctions(); + this.registeredFeatures.add(feature); + ((AbstractCommandFeature) feature).setCommandConfig(config); + } + + @Override + public void registerDefaultFeatures() { + YamlDocument document = plugin.getConfigManager().loadConfig(commandsFile); + try { + document.save(new File(plugin.getDataDirectory().toFile(), "commands.yml")); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.getFeatures().values().forEach(feature -> { + CommandConfig config = getCommandConfig(document, feature.getFeatureID()); + if (config.isEnable()) { + registerFeature(feature, config); + } + }); + } + + @Override + public void unregisterFeatures() { + this.registeredRootCommandComponents.forEach(component -> this.commandManager.commandRegistrationHandler().unregisterRootCommand(component)); + this.registeredRootCommandComponents.clear(); + this.registeredFeatures.forEach(CommandFeature::unregisterRelatedFunctions); + this.registeredFeatures.clear(); + } + + @Override + public CommandManager getCommandManager() { + return commandManager; + } + + @Override + public void handleCommandFeedback(C sender, TranslatableComponent.Builder key, Component... args) { + TranslatableComponent component = key.arguments(args).build(); + this.feedbackConsumer.accept(sender, component.key(), TranslationManager.render(component)); + } + + @Override + public void handleCommandFeedback(C sender, String node, Component component) { + this.feedbackConsumer.accept(sender, node, component); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/command/CommandBuilder.java b/common/src/main/java/net/momirealms/customcrops/common/command/CommandBuilder.java new file mode 100644 index 000000000..024021d60 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/command/CommandBuilder.java @@ -0,0 +1,58 @@ +/* + * 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.common.command; + +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public interface CommandBuilder { + + CommandBuilder setPermission(String permission); + + CommandBuilder setCommandNode(String... subNodes); + + Command.Builder getBuiltCommandBuilder(); + + class BasicCommandBuilder implements CommandBuilder { + + private Command.Builder commandBuilder; + + public BasicCommandBuilder(CommandManager commandManager, String rootNode) { + this.commandBuilder = commandManager.commandBuilder(rootNode); + } + + @Override + public CommandBuilder setPermission(String permission) { + this.commandBuilder = this.commandBuilder.permission(permission); + return this; + } + + @Override + public CommandBuilder setCommandNode(String... subNodes) { + for (String sub : subNodes) { + this.commandBuilder = this.commandBuilder.literal(sub); + } + return this; + } + + @Override + public Command.Builder getBuiltCommandBuilder() { + return commandBuilder; + } + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/command/CommandConfig.java b/common/src/main/java/net/momirealms/customcrops/common/command/CommandConfig.java new file mode 100644 index 000000000..76916f0b8 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/command/CommandConfig.java @@ -0,0 +1,77 @@ +/* + * 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.common.command; + +import java.util.ArrayList; +import java.util.List; + +public class CommandConfig { + + private boolean enable = false; + private List usages = new ArrayList<>(); + private String permission = null; + + private CommandConfig() { + } + + public CommandConfig(boolean enable, List usages, String permission) { + this.enable = enable; + this.usages = usages; + this.permission = permission; + } + + public boolean isEnable() { + return enable; + } + + public List getUsages() { + return usages; + } + + public String getPermission() { + return permission; + } + + public static class Builder { + + private final CommandConfig config; + + public Builder() { + this.config = new CommandConfig<>(); + } + + public Builder usages(List usages) { + config.usages = usages; + return this; + } + + public Builder permission(String permission) { + config.permission = permission; + return this; + } + + public Builder enable(boolean enable) { + config.enable = enable; + return this; + } + + public CommandConfig build() { + return config; + } + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customcrops/common/command/CommandFeature.java b/common/src/main/java/net/momirealms/customcrops/common/command/CommandFeature.java new file mode 100644 index 000000000..065cef2c9 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/command/CommandFeature.java @@ -0,0 +1,43 @@ +/* + * 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.common.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; + +public interface CommandFeature { + + Command registerCommand(CommandManager cloudCommandManager, Command.Builder builder); + + String getFeatureID(); + + void registerRelatedFunctions(); + + void unregisterRelatedFunctions(); + + void handleFeedback(CommandContext context, TranslatableComponent.Builder key, Component... args); + + void handleFeedback(C sender, TranslatableComponent.Builder key, Component... args); + + CustomCropsCommandManager getCustomCropsCommandManager(); + + CommandConfig getCommandConfig(); +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/command/CustomCropsCommandManager.java b/common/src/main/java/net/momirealms/customcrops/common/command/CustomCropsCommandManager.java new file mode 100644 index 000000000..b2317279f --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/command/CustomCropsCommandManager.java @@ -0,0 +1,56 @@ +/* + * 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.common.command; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.util.Index; +import net.momirealms.customcrops.common.util.TriConsumer; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public interface CustomCropsCommandManager { + + String commandsFile = "commands.yml"; + + void unregisterFeatures(); + + void registerFeature(CommandFeature feature, CommandConfig config); + + void registerDefaultFeatures(); + + Index> getFeatures(); + + void setFeedbackConsumer(@NotNull TriConsumer feedbackConsumer); + + TriConsumer defaultFeedbackConsumer(); + + CommandConfig getCommandConfig(YamlDocument document, String featureID); + + Collection> buildCommandBuilders(CommandConfig config); + + CommandManager getCommandManager(); + + void handleCommandFeedback(C sender, TranslatableComponent.Builder key, Component... args); + + void handleCommandFeedback(C sender, String node, Component component); +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/config/ConfigLoader.java b/common/src/main/java/net/momirealms/customcrops/common/config/ConfigLoader.java new file mode 100644 index 000000000..e834e72f9 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/config/ConfigLoader.java @@ -0,0 +1,69 @@ +/* + * 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.common.config; + +import dev.dejvokep.boostedyaml.YamlDocument; + +import java.io.File; + +/** + * Interface for loading and managing configuration files. + */ +public interface ConfigLoader { + + /** + * Loads a YAML configuration file from the specified file path. + * + * @param filePath the path to the configuration file + * @return the loaded {@link YamlDocument} + */ + YamlDocument loadConfig(String filePath); + + /** + * Loads a YAML configuration file from the specified file path with a custom route separator. + * + * @param filePath the path to the configuration file + * @param routeSeparator the custom route separator character + * @return the loaded {@link YamlDocument} + */ + YamlDocument loadConfig(String filePath, char routeSeparator); + + /** + * Loads a YAML data file. + * + * @param file the {@link File} object representing the data file + * @return the loaded {@link YamlDocument} + */ + YamlDocument loadData(File file); + + /** + * Loads a YAML data file with a custom route separator. + * + * @param file the {@link File} object representing the data file + * @param routeSeparator the custom route separator character + * @return the loaded {@link YamlDocument} + */ + YamlDocument loadData(File file, char routeSeparator); + + /** + * Saves a resource file from the plugin's jar to the specified file path. + * + * @param filePath the path where the resource file will be saved + */ + void saveResource(String filePath); +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/dependency/Dependency.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/Dependency.java new file mode 100644 index 000000000..a55794b10 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/Dependency.java @@ -0,0 +1,268 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.dependency; + +import net.momirealms.customcrops.common.dependency.relocation.Relocation; +import net.momirealms.customcrops.common.plugin.CustomCropsProperties; +import org.jetbrains.annotations.Nullable; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * The dependencies used by CustomCrops. + */ +public enum Dependency { + + ASM( + "org.ow2.asm", + "asm", + "maven", + "asm" + ), + ASM_COMMONS( + "org.ow2.asm", + "asm-commons", + "maven", + "asm-commons" + ), + JAR_RELOCATOR( + "me.lucko", + "jar-relocator", + "maven", + "jar-relocator" + ), + CLOUD_CORE( + "org{}incendo", + "cloud-core", + "maven", + "cloud-core", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_BRIGADIER( + "org{}incendo", + "cloud-brigadier", + "maven", + "cloud-brigadier", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_SERVICES( + "org{}incendo", + "cloud-services", + "maven", + "cloud-services", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_BUKKIT( + "org{}incendo", + "cloud-bukkit", + "maven", + "cloud-bukkit", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_PAPER( + "org{}incendo", + "cloud-paper", + "maven", + "cloud-paper", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_MINECRAFT_EXTRAS( + "org{}incendo", + "cloud-minecraft-extras", + "maven", + "cloud-minecraft-extras", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("adventure", "net{}kyori{}adventure"), + Relocation.of("option", "net{}kyori{}option"), + Relocation.of("examination", "net{}kyori{}examination"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + GEANTY_REF( + "io{}leangen{}geantyref", + "geantyref", + "maven", + "geantyref", + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + BOOSTED_YAML( + "dev{}dejvokep", + "boosted-yaml", + "maven", + "boosted-yaml", + Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") + ), + BSTATS_BASE( + "org{}bstats", + "bstats-base", + "maven", + "bstats-base", + Relocation.of("bstats", "org{}bstats") + ), + BSTATS_BUKKIT( + "org{}bstats", + "bstats-bukkit", + "maven", + "bstats-bukkit", + Relocation.of("bstats", "org{}bstats") + ) { + @Override + public String getVersion() { + return Dependency.BSTATS_BASE.getVersion(); + } + }, + GSON( + "com.google.code.gson", + "gson", + "maven", + "gson" + ), + CAFFEINE( + "com{}github{}ben-manes{}caffeine", + "caffeine", + "maven", + "caffeine", + Relocation.of("caffeine", "com{}github{}benmanes{}caffeine") + ), + EXP4J( + "net{}objecthunter", + "exp4j", + "maven", + "exp4j", + Relocation.of("exp4j", "net{}objecthunter{}exp4j") + ), + SLF4J_SIMPLE( + "org.slf4j", + "slf4j-simple", + "maven", + "slf4j_simple" + ) { + @Override + public String getVersion() { + return Dependency.SLF4J_API.getVersion(); + } + }, + SLF4J_API( + "org.slf4j", + "slf4j-api", + "maven", + "slf4j" + ), + ZSTD( + "com.github.luben", + "zstd-jni", + "maven", + "zstd-jni" + ); + + private final List relocations; + private final String repo; + private final String groupId; + private String rawArtifactId; + private String customArtifactID; + + private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; + + Dependency(String groupId, String rawArtifactId, String repo, String customArtifactID) { + this(groupId, rawArtifactId, repo, customArtifactID, new Relocation[0]); + } + + Dependency(String groupId, String rawArtifactId, String repo, String customArtifactID, Relocation... relocations) { + this.rawArtifactId = rawArtifactId; + this.groupId = groupId; + this.relocations = new ArrayList<>(Arrays.stream(relocations).toList()); + this.repo = repo; + this.customArtifactID = customArtifactID; + } + + public Dependency setCustomArtifactID(String customArtifactID) { + this.customArtifactID = customArtifactID; + return this; + } + + public Dependency setRawArtifactID(String artifactId) { + this.rawArtifactId = artifactId; + return this; + } + + public String getVersion() { + return CustomCropsProperties.getValue(customArtifactID); + } + + private static String rewriteEscaping(String s) { + return s.replace("{}", "."); + } + + public String getFileName(String classifier) { + String name = customArtifactID.toLowerCase(Locale.ROOT).replace('_', '-'); + String extra = classifier == null || classifier.isEmpty() + ? "" + : "-" + classifier; + return name + "-" + this.getVersion() + extra + ".jar"; + } + + String getMavenRepoPath() { + return String.format(MAVEN_FORMAT, + rewriteEscaping(groupId).replace(".", "/"), + rewriteEscaping(rawArtifactId), + getVersion(), + rewriteEscaping(rawArtifactId), + getVersion() + ); + } + + public List getRelocations() { + return this.relocations; + } + + /** + * Creates a {@link MessageDigest} suitable for computing the checksums + * of dependencies. + * + * @return the digest + */ + public static MessageDigest createDigest() { + try { + return MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + @Nullable + public String getRepo() { + return repo; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyDownloadException.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyDownloadException.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyDownloadException.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyDownloadException.java index 515e58972..16bc6470a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyDownloadException.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyDownloadException.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies; +package net.momirealms.customcrops.common.dependency; /** * Exception thrown if a dependency cannot be downloaded. diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManager.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyManager.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManager.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyManager.java index 93c7a778b..0b8045ee9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManager.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyManager.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies; +package net.momirealms.customcrops.common.dependency; import java.util.Collection; import java.util.Set; diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyManagerImpl.java similarity index 69% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyManagerImpl.java index dc8ee83bd..cbf177add 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyManagerImpl.java @@ -23,18 +23,15 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies; - -import com.google.common.collect.ImmutableSet; -import net.momirealms.customcrops.CustomCropsPluginImpl; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.libraries.classpath.ClassPathAppender; -import net.momirealms.customcrops.libraries.dependencies.classloader.IsolatedClassLoader; -import net.momirealms.customcrops.libraries.dependencies.relocation.Relocation; -import net.momirealms.customcrops.libraries.dependencies.relocation.RelocationHandler; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -import java.io.File; +package net.momirealms.customcrops.common.dependency; + +import net.momirealms.customcrops.common.dependency.classloader.IsolatedClassLoader; +import net.momirealms.customcrops.common.dependency.relocation.Relocation; +import net.momirealms.customcrops.common.dependency.relocation.RelocationHandler; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; +import net.momirealms.customcrops.common.plugin.classpath.ClassPathAppender; +import net.momirealms.customcrops.common.util.FileUtils; + import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -42,6 +39,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; /** * Loads and manages runtime dependencies for the plugin. @@ -57,20 +55,24 @@ public class DependencyManagerImpl implements DependencyManager { /** A map of dependencies which have already been loaded. */ private final EnumMap loaded = new EnumMap<>(Dependency.class); /** A map of isolated classloaders which have been created. */ - private final Map, IsolatedClassLoader> loaders = new HashMap<>(); + private final Map, IsolatedClassLoader> loaders = new HashMap<>(); /** Cached relocation handler instance. */ - private @MonotonicNonNull RelocationHandler relocationHandler = null; + private final RelocationHandler relocationHandler; + private final Executor loadingExecutor; + private final CustomCropsPlugin plugin; - public DependencyManagerImpl(CustomCropsPluginImpl plugin, ClassPathAppender classPathAppender) { + public DependencyManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; this.registry = new DependencyRegistry(); this.cacheDirectory = setupCacheDirectory(plugin); - this.classPathAppender = classPathAppender; + this.classPathAppender = plugin.getClassPathAppender(); + this.loadingExecutor = plugin.getScheduler().async(); this.relocationHandler = new RelocationHandler(this); } @Override public ClassLoader obtainClassLoaderWith(Set dependencies) { - ImmutableSet set = ImmutableSet.copyOf(dependencies); + Set set = new HashSet<>(dependencies); for (Dependency dependency : dependencies) { if (!this.loaded.containsKey(dependency)) { @@ -111,13 +113,15 @@ public void loadDependencies(Collection dependencies) { continue; } - try { - loadDependency(dependency); - } catch (Throwable e) { - new RuntimeException("Unable to load dependency " + dependency.name(), e).printStackTrace(); - } finally { - latch.countDown(); - } + this.loadingExecutor.execute(() -> { + try { + loadDependency(dependency); + } catch (Throwable e) { + this.plugin.getPluginLogger().warn("Unable to load dependency " + dependency.name(), e); + } finally { + latch.countDown(); + } + }); } try { @@ -142,7 +146,8 @@ private void loadDependency(Dependency dependency) throws Exception { } private Path downloadDependency(Dependency dependency) throws DependencyDownloadException { - Path file = this.cacheDirectory.resolve(dependency.getFileName(null)); + String fileName = dependency.getFileName(null); + Path file = this.cacheDirectory.resolve(fileName); // if the file already exists, don't attempt to re-download it. if (Files.exists(file)) { @@ -150,30 +155,19 @@ private Path downloadDependency(Dependency dependency) throws DependencyDownload } DependencyDownloadException lastError = null; - String fileName = dependency.getFileName(null); String forceRepo = dependency.getRepo(); - if (forceRepo == null) { - // attempt to download the dependency from each repo in order. - for (DependencyRepository repo : DependencyRepository.values()) { + List repository = DependencyRepository.getByID(forceRepo); + if (!repository.isEmpty()) { + int i = 0; + while (i < repository.size()) { try { - LogUtils.info("Downloading dependency(" + fileName + ") from " + repo.getUrl() + dependency.getMavenRepoPath()); - repo.download(dependency, file); - LogUtils.info("Successfully downloaded " + fileName); - return file; - } catch (DependencyDownloadException e) { - lastError = e; - } - } - } else { - DependencyRepository repository = DependencyRepository.getByID(forceRepo); - if (repository != null) { - try { - LogUtils.info("Downloading dependency(" + fileName + ") from " + repository.getUrl() + dependency.getMavenRepoPath()); - repository.download(dependency, file); - LogUtils.info("Successfully downloaded " + fileName); + plugin.getPluginLogger().info("Downloading dependency(" + fileName + ")[" + repository.get(i).getUrl() + dependency.getMavenRepoPath() + "]"); + repository.get(i).download(dependency, file); + plugin.getPluginLogger().info("Successfully downloaded " + fileName); return file; } catch (DependencyDownloadException e) { lastError = e; + i++; } } } @@ -193,16 +187,21 @@ private Path remapDependency(Dependency dependency, Path normalFile) throws Exce return remappedFile; } - LogUtils.info("Remapping " + dependency.getFileName(null)); + plugin.getPluginLogger().info("Remapping " + dependency.getFileName(null)); relocationHandler.remap(normalFile, remappedFile, rules); - LogUtils.info("Successfully remapped " + dependency.getFileName(null)); + plugin.getPluginLogger().info("Successfully remapped " + dependency.getFileName(null)); return remappedFile; } - private static Path setupCacheDirectory(CustomCropsPluginImpl plugin) { - File folder = new File(plugin.getDataFolder(), "libs"); - folder.mkdirs(); - return folder.toPath(); + private static Path setupCacheDirectory(CustomCropsPlugin plugin) { + Path cacheDirectory = plugin.getDataDirectory().resolve("libs"); + try { + FileUtils.createDirectoriesIfNotExists(cacheDirectory); + } catch (IOException e) { + throw new RuntimeException("Unable to create libs directory", e); + } + + return cacheDirectory; } @Override @@ -222,8 +221,7 @@ public void close() { } if (firstEx != null) { - firstEx.printStackTrace(); + plugin.getPluginLogger().severe(firstEx.getMessage(), firstEx); } } - } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyRegistry.java similarity index 93% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyRegistry.java index e2714fc7d..8c1340178 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyRegistry.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies; +package net.momirealms.customcrops.common.dependency; import com.google.gson.JsonElement; @@ -36,7 +36,7 @@ public boolean shouldAutoLoad(Dependency dependency) { return switch (dependency) { // all used within 'isolated' classloaders, and are therefore not // relocated. - case ASM, ASM_COMMONS, JAR_RELOCATOR, H2_DRIVER, SQLITE_DRIVER -> false; + case ASM, ASM_COMMONS, JAR_RELOCATOR, ZSTD -> false; default -> true; }; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyRepository.java similarity index 86% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyRepository.java index d41347f22..667768f2d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/DependencyRepository.java @@ -23,9 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies; - -import com.google.common.io.ByteStreams; +package net.momirealms.customcrops.common.dependency; import java.io.IOException; import java.io.InputStream; @@ -33,6 +31,10 @@ import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; /** * Represents a repository which contains {@link Dependency}s. @@ -54,15 +56,7 @@ protected URLConnection openConnection(Dependency dependency) throws IOException /** * Maven Central Mirror */ - MAVEN_CENTRAL_MIRROR("aliyun", "https://maven.aliyun.com/repository/public/"), - /** - * Code MC - */ - CODE_MC("codemc", "https://repo.codemc.io/repository/maven-public/"), - /** - * Jitpack - */ - JITPACK("jitpack", "https://jitpack.io/"); + MAVEN_CENTRAL_MIRROR("maven", "https://maven.aliyun.com/repository/public/"); private final String url; private final String id; @@ -76,13 +70,18 @@ public String getUrl() { return url; } - public static DependencyRepository getByID(String id) { + public static List getByID(String id) { + ArrayList repositories = new ArrayList<>(); for (DependencyRepository repository : values()) { if (id.equals(repository.id)) { - return repository; + repositories.add(repository); } } - return null; + // 中国大陆优先使用阿里云镜像 + if (Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) { + Collections.reverse(repositories); + } + return repositories; } /** @@ -108,7 +107,7 @@ public byte[] downloadRaw(Dependency dependency) throws DependencyDownloadExcept try { URLConnection connection = openConnection(dependency); try (InputStream in = connection.getInputStream()) { - byte[] bytes = ByteStreams.toByteArray(in); + byte[] bytes = in.readAllBytes(); if (bytes.length == 0) { throw new DependencyDownloadException("Empty stream"); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/classloader/IsolatedClassLoader.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/classloader/IsolatedClassLoader.java index ad4c9624e..09e7a7471 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/classloader/IsolatedClassLoader.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies.classloader; +package net.momirealms.customcrops.common.dependency.classloader; import java.net.URL; import java.net.URLClassLoader; diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/Relocation.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/Relocation.java similarity index 97% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/Relocation.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/Relocation.java index 3578cc064..b1e10bc1e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/Relocation.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/Relocation.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies.relocation; +package net.momirealms.customcrops.common.dependency.relocation; import java.util.Objects; diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHandler.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/RelocationHandler.java similarity index 89% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHandler.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/RelocationHandler.java index 2320e85df..02298ea95 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHandler.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/RelocationHandler.java @@ -23,11 +23,11 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies.relocation; +package net.momirealms.customcrops.common.dependency.relocation; -import net.momirealms.customcrops.libraries.dependencies.Dependency; -import net.momirealms.customcrops.libraries.dependencies.DependencyManager; -import net.momirealms.customcrops.libraries.dependencies.classloader.IsolatedClassLoader; +import net.momirealms.customcrops.common.dependency.Dependency; +import net.momirealms.customcrops.common.dependency.DependencyManager; +import net.momirealms.customcrops.common.dependency.classloader.IsolatedClassLoader; import java.io.File; import java.io.IOException; @@ -66,8 +66,8 @@ public RelocationHandler(DependencyManager dependencyManager) { this.jarRelocatorRunMethod.setAccessible(true); } catch (Exception e) { try { - if (classLoader instanceof IsolatedClassLoader) { - ((IsolatedClassLoader) classLoader).close(); + if (classLoader instanceof IsolatedClassLoader isolatedClassLoader) { + isolatedClassLoader.close(); } } catch (IOException ex) { e.addSuppressed(ex); @@ -87,5 +87,4 @@ public void remap(Path input, Path output, List relocations) throws Object relocator = this.jarRelocatorConstructor.newInstance(input.toFile(), output.toFile(), mappings); this.jarRelocatorRunMethod.invoke(relocator); } - } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHelper.java b/common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/RelocationHelper.java similarity index 95% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHelper.java rename to common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/RelocationHelper.java index c5e64ea14..fb2cf124b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHelper.java +++ b/common/src/main/java/net/momirealms/customcrops/common/dependency/relocation/RelocationHelper.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.dependencies.relocation; +package net.momirealms.customcrops.common.dependency.relocation; public final class RelocationHelper { @@ -32,6 +32,5 @@ public final class RelocationHelper { public static final String OKHTTP3_STRING = String.valueOf(new char[]{'o', 'k', 'h', 't', 't', 'p', '3'}); private RelocationHelper() { - } } diff --git a/common/src/main/java/net/momirealms/customcrops/common/helper/AdventureHelper.java b/common/src/main/java/net/momirealms/customcrops/common/helper/AdventureHelper.java new file mode 100644 index 000000000..bfa728e88 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/helper/AdventureHelper.java @@ -0,0 +1,285 @@ +/* + * 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.common.helper; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.title.Title; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +/** + * Helper class for handling Adventure components and related functionalities. + */ +public class AdventureHelper { + + private final MiniMessage miniMessage; + private final MiniMessage miniMessageStrict; + private final GsonComponentSerializer gsonComponentSerializer; + private final Cache miniMessageToJsonCache = Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(); + public static boolean legacySupport = false; + + private AdventureHelper() { + this.miniMessage = MiniMessage.builder().build(); + this.miniMessageStrict = MiniMessage.builder().strict(true).build(); + this.gsonComponentSerializer = GsonComponentSerializer.builder().build(); + } + + private static class SingletonHolder { + private static final AdventureHelper INSTANCE = new AdventureHelper(); + } + + /** + * Retrieves the singleton instance of AdventureHelper. + * + * @return the singleton instance + */ + public static AdventureHelper getInstance() { + return SingletonHolder.INSTANCE; + } + + /** + * Converts a MiniMessage string to a Component. + * + * @param text the MiniMessage string + * @return the resulting Component + */ + public static Component miniMessage(String text) { + if (legacySupport) { + return getMiniMessage().deserialize(legacyToMiniMessage(text)); + } else { + return getMiniMessage().deserialize(text); + } + } + + /** + * Retrieves the MiniMessage instance. + * + * @return the MiniMessage instance + */ + public static MiniMessage getMiniMessage() { + return getInstance().miniMessage; + } + + /** + * Retrieves the GsonComponentSerializer instance. + * + * @return the GsonComponentSerializer instance + */ + public static GsonComponentSerializer getGson() { + return getInstance().gsonComponentSerializer; + } + + /** + * Converts a MiniMessage string to a JSON string. + * + * @param miniMessage the MiniMessage string + * @return the JSON string representation + */ + public static String miniMessageToJson(String miniMessage) { + AdventureHelper instance = getInstance(); + return instance.miniMessageToJsonCache.get(miniMessage, (text) -> instance.gsonComponentSerializer.serialize(miniMessage(text))); + } + + /** + * Sends a title to an audience. + * + * @param audience the audience to send the title to + * @param title the title component + * @param subtitle the subtitle component + * @param fadeIn the fade-in duration in ticks + * @param stay the stay duration in ticks + * @param fadeOut the fade-out duration in ticks + */ + public static void sendTitle(Audience audience, Component title, Component subtitle, int fadeIn, int stay, int fadeOut) { + audience.showTitle(Title.title(title, subtitle, Title.Times.times(Duration.ofMillis(fadeIn * 50L), Duration.ofMillis(stay * 50L), Duration.ofMillis(fadeOut * 50L)))); + } + + /** + * Sends an action bar message to an audience. + * + * @param audience the audience to send the action bar message to + * @param actionBar the action bar component + */ + public static void sendActionBar(Audience audience, Component actionBar) { + audience.sendActionBar(actionBar); + } + + /** + * Sends a message to an audience. + * + * @param audience the audience to send the message to + * @param message the message component + */ + public static void sendMessage(Audience audience, Component message) { + audience.sendMessage(message); + } + + /** + * Plays a sound for an audience. + * + * @param audience the audience to play the sound for + * @param sound the sound to play + */ + public static void playSound(Audience audience, Sound sound) { + audience.playSound(sound); + } + + /** + * Surrounds text with a MiniMessage font tag. + * + * @param text the text to surround + * @param font the font as a {@link Key} + * @return the text surrounded by the MiniMessage font tag + */ + public static String surroundWithMiniMessageFont(String text, Key font) { + return "" + text + ""; + } + + /** + * Surrounds text with a MiniMessage font tag. + * + * @param text the text to surround + * @param font the font as a {@link String} + * @return the text surrounded by the MiniMessage font tag + */ + public static String surroundWithMiniMessageFont(String text, String font) { + return "" + text + ""; + } + + /** + * Converts a JSON string to a MiniMessage string. + * + * @param json the JSON string + * @return the MiniMessage string representation + */ + public static String jsonToMiniMessage(String json) { + return getInstance().miniMessageStrict.serialize(getInstance().gsonComponentSerializer.deserialize(json)); + } + + /** + * Converts a JSON string to a Component. + * + * @param json the JSON string + * @return the resulting Component + */ + public static Component jsonToComponent(String json) { + return getInstance().gsonComponentSerializer.deserialize(json); + } + + /** + * Converts a Component to a JSON string. + * + * @param component the Component to convert + * @return the JSON string representation + */ + public static String componentToJson(Component component) { + return getGson().serialize(component); + } + + /** + * Checks if a character is a legacy color code. + * + * @param c the character to check + * @return true if the character is a color code, false otherwise + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean isLegacyColorCode(char c) { + return c == '§' || c == '&'; + } + + /** + * Converts a legacy color code string to a MiniMessage string. + * + * @param legacy the legacy color code string + * @return the MiniMessage string representation + */ + public static String legacyToMiniMessage(String legacy) { + StringBuilder stringBuilder = new StringBuilder(); + char[] chars = legacy.toCharArray(); + for (int i = 0; i < chars.length; i++) { + if (!isLegacyColorCode(chars[i])) { + stringBuilder.append(chars[i]); + continue; + } + if (i + 1 >= chars.length) { + stringBuilder.append(chars[i]); + continue; + } + switch (chars[i+1]) { + case '0' -> stringBuilder.append(""); + case '1' -> stringBuilder.append(""); + case '2' -> stringBuilder.append(""); + case '3' -> stringBuilder.append(""); + case '4' -> stringBuilder.append(""); + case '5' -> stringBuilder.append(""); + case '6' -> stringBuilder.append(""); + case '7' -> stringBuilder.append(""); + case '8' -> stringBuilder.append(""); + case '9' -> stringBuilder.append(""); + case 'a' -> stringBuilder.append(""); + case 'b' -> stringBuilder.append(""); + case 'c' -> stringBuilder.append(""); + case 'd' -> stringBuilder.append(""); + case 'e' -> stringBuilder.append(""); + case 'f' -> stringBuilder.append(""); + case 'r' -> stringBuilder.append(""); + case 'l' -> stringBuilder.append(""); + case 'm' -> stringBuilder.append(""); + case 'o' -> stringBuilder.append(""); + case 'n' -> stringBuilder.append(""); + case 'k' -> stringBuilder.append(""); + case 'x' -> { + if (i + 13 >= chars.length + || !isLegacyColorCode(chars[i+2]) + || !isLegacyColorCode(chars[i+4]) + || !isLegacyColorCode(chars[i+6]) + || !isLegacyColorCode(chars[i+8]) + || !isLegacyColorCode(chars[i+10]) + || !isLegacyColorCode(chars[i+12])) { + stringBuilder.append(chars[i]); + continue; + } + stringBuilder + .append("<#") + .append(chars[i+3]) + .append(chars[i+5]) + .append(chars[i+7]) + .append(chars[i+9]) + .append(chars[i+11]) + .append(chars[i+13]) + .append(">"); + i += 12; + } + default -> { + stringBuilder.append(chars[i]); + continue; + } + } + i++; + } + return stringBuilder.toString(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java b/common/src/main/java/net/momirealms/customcrops/common/helper/ExpressionHelper.java similarity index 55% rename from api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java rename to common/src/main/java/net/momirealms/customcrops/common/helper/ExpressionHelper.java index 53004bf7f..38b7a1aa7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java +++ b/common/src/main/java/net/momirealms/customcrops/common/helper/ExpressionHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,23 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.mechanic.item.fertilizer; +package net.momirealms.customcrops.common.helper; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.objecthunter.exp4j.ExpressionBuilder; -public interface QualityCrop extends Fertilizer { - - /** - * Get the chance of taking effect - * - * @return chance - */ - double getChance(); +/** + * Helper class for evaluating mathematical expressions. + */ +public class ExpressionHelper { /** - * Get the modified quality ratio + * Evaluates a mathematical expression provided as a string. * - * @return the modified quality ratio + * @param expression the mathematical expression to evaluate + * @return the result of the evaluation as a double */ - double[] getRatio(); + public static double evaluate(String expression) { + return new ExpressionBuilder(expression).build().evaluate(); + } } diff --git a/common/src/main/java/net/momirealms/customcrops/common/helper/GsonHelper.java b/common/src/main/java/net/momirealms/customcrops/common/helper/GsonHelper.java new file mode 100644 index 000000000..c87010387 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/helper/GsonHelper.java @@ -0,0 +1,59 @@ +/* + * 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.common.helper; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Helper class for managing Gson instances. + */ +public class GsonHelper { + + private final Gson gson; + + public GsonHelper() { + this.gson = new GsonBuilder() + .create(); + } + + /** + * Retrieves the Gson instance. + * + * @return the Gson instance + */ + public Gson getGson() { + return gson; + } + + /** + * Retrieves the singleton Gson instance from GsonHelper. + * + * @return the singleton Gson instance + */ + public static Gson get() { + return SingletonHolder.INSTANCE.getGson(); + } + + /** + * Static inner class for holding the singleton instance of GsonHelper. + */ + private static class SingletonHolder { + private static final GsonHelper INSTANCE = new GsonHelper(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java b/common/src/main/java/net/momirealms/customcrops/common/helper/VersionHelper.java similarity index 64% rename from plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java rename to common/src/main/java/net/momirealms/customcrops/common/helper/VersionHelper.java index 47b1b96a6..3bfb8b9a2 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java +++ b/common/src/main/java/net/momirealms/customcrops/common/helper/VersionHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,11 +15,9 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.manager; +package net.momirealms.customcrops.common.helper; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.VersionManager; -import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; import java.io.BufferedReader; import java.io.InputStream; @@ -27,119 +25,101 @@ import java.net.URL; import java.net.URLConnection; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; -public class VersionManagerImpl extends VersionManager { - - private final CustomCropsPlugin plugin; - private final String pluginVersion; - private boolean foliaScheduler; - private final boolean isSpigot; - private boolean isMojmap; - - private final float mcVersion; +/** + * This class implements the VersionManager interface and is responsible for managing version-related information. + */ +public class VersionHelper { - @SuppressWarnings("deprecation") - public VersionManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.isSpigot = plugin.getServer().getName().equals("CraftBukkit"); - this.pluginVersion = plugin.getDescription().getVersion(); + // Method to asynchronously check for plugin updates + public static final Function> UPDATE_CHECKER = (plugin) -> { + CompletableFuture updateFuture = new CompletableFuture<>(); + plugin.getScheduler().async().execute(() -> { + try { + URL url = new URL("https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=2625&key=version"); + URLConnection conn = url.openConnection(); + conn.setConnectTimeout(10000); + conn.setReadTimeout(60000); + InputStream inputStream = conn.getInputStream(); + String newest = new BufferedReader(new InputStreamReader(inputStream)).readLine(); + String current = plugin.getPluginVersion(); + inputStream.close(); + if (!compareVer(newest, current)) { + updateFuture.complete(false); + return; + } + updateFuture.complete(true); + } catch (Exception exception) { + plugin.getPluginLogger().warn("Error occurred when checking update.", exception); + updateFuture.complete(false); + } + }); + return updateFuture; + }; - String[] split = plugin.getServerVersion().split("\\."); - this.mcVersion = Float.parseFloat(split[1] + "." + (split.length >= 3 ? split[2] : "0")); + private static float version; + private static boolean mojmap; + private static boolean folia; - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); - this.foliaScheduler = true; - } catch (ClassNotFoundException ignored) { - this.foliaScheduler = false; - } + public static void init(String serverVersion) { + String[] split = serverVersion.split("\\."); + version = Float.parseFloat(split[1] + "." + (split.length == 3 ? split[2] : "0")); + checkMojMap(); + checkFolia(); + } + private static void checkMojMap() { // Check if the server is Mojmap try { Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket"); - this.isMojmap = true; + mojmap = true; } catch (ClassNotFoundException ignored) { - } } - @Override - public boolean hasRegionScheduler() { - return foliaScheduler; - } - - @Override - public String getPluginVersion() { - return pluginVersion; - } - - @Override - public boolean isSpigot() { - return isSpigot; + private static void checkFolia() { + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + folia = true; + } catch (ClassNotFoundException ignored) { + } } - @Override - public boolean isVersionNewerThan1_19_R3() { - return mcVersion >= 19.4; + public static boolean isVersionNewerThan1_18() { + return version >= 18; } - @Override - public boolean isVersionNewerThan1_20_R2() { - return mcVersion >= 20.2; + public static boolean isVersionNewerThan1_19() { + return version >= 19; } - @Override - public boolean isVersionNewerThan1_19() { - return mcVersion >= 19; + public static boolean isVersionNewerThan1_19_4() { + return version >= 19.4; } - @Override - public boolean isVersionNewerThan1_19_R2() { - return mcVersion >= 19.3; + public static boolean isVersionNewerThan1_19_3() { + return version >= 19.3; } - @Override - public boolean isVersionNewerThan1_20() { - return mcVersion >= 20; + public static boolean isVersionNewerThan1_20() { + return version >= 20.0; } - @Override - public boolean isVersionNewerThan1_18() { - return mcVersion >= 18; + public static boolean isVersionNewerThan1_20_5() { + return version >= 20.5; } - @Override - public boolean isMojmap() { - return isMojmap; + public static boolean isFolia() { + return folia; } - @Override - public CompletableFuture checkUpdate() { - CompletableFuture updateFuture = new CompletableFuture<>(); - plugin.getScheduler().runTaskAsync(() -> { - try { - URL url = new URL("https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=2625&key=version"); - URLConnection conn = url.openConnection(); - conn.setConnectTimeout(10000); - conn.setReadTimeout(60000); - InputStream inputStream = conn.getInputStream(); - String newest = new BufferedReader(new InputStreamReader(inputStream)).readLine(); - String current = plugin.getVersionManager().getPluginVersion(); - inputStream.close(); - if (!compareVer(newest, current)) { - updateFuture.complete(false); - return; - } - updateFuture.complete(true); - } catch (Exception exception) { - LogUtils.warn("Error occurred when checking update.", exception); - updateFuture.complete(false); - } - }); - return updateFuture; + public static boolean isMojmap() { + return mojmap; } - private boolean compareVer(String newV, String currentV) { + // Method to compare two version strings + private static boolean compareVer(String newV, String currentV) { if (newV == null || currentV == null || newV.isEmpty() || currentV.isEmpty()) { return false; } @@ -183,4 +163,4 @@ else if (newHotfix.length > 1 && currentHotfix.length > 1) { } return newVS.length > currentVS.length; } -} +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/AbstractItem.java b/common/src/main/java/net/momirealms/customcrops/common/item/AbstractItem.java similarity index 69% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/AbstractItem.java rename to common/src/main/java/net/momirealms/customcrops/common/item/AbstractItem.java index 4deb2c089..a3ee1eaf4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/AbstractItem.java +++ b/common/src/main/java/net/momirealms/customcrops/common/item/AbstractItem.java @@ -1,6 +1,23 @@ -package net.momirealms.customcrops.mechanic.item.factory; - -import net.momirealms.customcrops.api.CustomCropsPlugin; +/* + * 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.common.item; + +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; import java.util.List; import java.util.Optional; @@ -17,17 +34,6 @@ public class AbstractItem implements Item { this.item = item; } - @Override - public Item customModelData(Integer data) { - factory.customModelData(item, data); - return this; - } - - @Override - public Optional customModelData() { - return factory.customModelData(item); - } - @Override public Item damage(Integer data) { factory.damage(item, data); @@ -50,6 +56,17 @@ public Optional maxDamage() { return factory.maxDamage(item); } + @Override + public Item customModelData(Integer data) { + factory.customModelData(item, data); + return this; + } + + @Override + public Optional customModelData() { + return factory.customModelData(item); + } + @Override public Item lore(List lore) { factory.lore(item, lore); @@ -61,6 +78,12 @@ public Optional> lore() { return factory.lore(item); } + + @Override + public boolean unbreakable() { + return factory.unbreakable(item); + } + @Override public Optional getTag(Object... path) { return factory.getTag(item, path); @@ -101,4 +124,8 @@ public I loadCopy() { public void update() { factory.update(item); } + + public R getRTagItem() { + return item; + } } diff --git a/common/src/main/java/net/momirealms/customcrops/common/item/ComponentKeys.java b/common/src/main/java/net/momirealms/customcrops/common/item/ComponentKeys.java new file mode 100644 index 000000000..f6f5d99b9 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/item/ComponentKeys.java @@ -0,0 +1,32 @@ +/* + * 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.common.item; + +import net.kyori.adventure.key.Key; + +public class ComponentKeys { + + public static final String CUSTOM_MODEL_DATA = Key.key("minecraft", "custom_model_data").asString(); + public static final String CUSTOM_NAME = Key.key("minecraft", "custom_name").asString(); + public static final String LORE = Key.key("minecraft", "lore").asString(); + public static final String DAMAGE = Key.key("minecraft", "damage").asString(); + public static final String MAX_DAMAGE = Key.key("minecraft", "max_damage").asString(); + public static final String ENCHANTMENT_GLINT_OVERRIDE = Key.key("minecraft", "enchantment_glint_override").asString(); + public static final String ENCHANTMENTS = Key.key("minecraft", "enchantments").asString(); + public static final String STORED_ENCHANTMENTS = Key.key("minecraft", "stored_enchantments").asString(); +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/item/Item.java b/common/src/main/java/net/momirealms/customcrops/common/item/Item.java new file mode 100644 index 000000000..19b2d850e --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/item/Item.java @@ -0,0 +1,157 @@ +/* + * 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.common.item; + +import java.util.List; +import java.util.Optional; + +/** + * Interface representing an item. + * This interface provides methods for managing item properties such as custom model data, + * damage, display name, lore, enchantments, and tags. + * + * @param the type of the item implementation + */ +public interface Item { + + /** + * Sets the custom model data for the item. + * + * @param data the custom model data to set + * @return the current {@link Item} instance for method chaining + */ + Item customModelData(Integer data); + + /** + * Retrieves the custom model data of the item. + * + * @return an {@link Optional} containing the custom model data, or empty if not set + */ + Optional customModelData(); + + /** + * Sets the damage value for the item. + * + * @param data the damage value to set + * @return the current {@link Item} instance for method chaining + */ + Item damage(Integer data); + + /** + * Retrieves the damage value of the item. + * + * @return an {@link Optional} containing the damage value, or empty if not set + */ + Optional damage(); + + /** + * Sets the maximum damage value for the item. + * + * @param data the maximum damage value to set + * @return the current {@link Item} instance for method chaining + */ + Item maxDamage(Integer data); + + /** + * Retrieves the maximum damage value of the item. + * + * @return an {@link Optional} containing the maximum damage value, or empty if not set + */ + Optional maxDamage(); + + /** + * Sets the lore for the item. + * + * @param lore the lore to set + * @return the current {@link Item} instance for method chaining + */ + Item lore(List lore); + + /** + * Retrieves the lore of the item. + * + * @return an {@link Optional} containing the lore, or empty if not set + */ + Optional> lore(); + + /** + * Checks if the item is unbreakable. + * + * @return true if the item is unbreakable, false otherwise + */ + boolean unbreakable(); + + /** + * Retrieves the tag value at the specified path. + * + * @param path the path to the tag value + * @return an {@link Optional} containing the tag value, or empty if not found + */ + Optional getTag(Object... path); + + /** + * Sets the tag value at the specified path. + * + * @param value the value to set + * @param path the path to the tag value + * @return the current {@link Item} instance for method chaining + */ + Item setTag(Object value, Object... path); + + /** + * Checks if the item has a tag value at the specified path. + * + * @param path the path to the tag value + * @return true if the tag value exists, false otherwise + */ + boolean hasTag(Object... path); + + /** + * Removes the tag value at the specified path. + * + * @param path the path to the tag value + * @return true if the tag was removed, false otherwise + */ + boolean removeTag(Object... path); + + /** + * Retrieves the underlying item implementation. + * + * @return the item implementation of type {@link I} + */ + I getItem(); + + /** + * Loads changes to the item. + * + * @return the loaded item implementation of type {@link I} + */ + I load(); + + /** + * Loads the changes and gets a copy of the item. + * + * @return a copy of the loaded item implementation of type {@link I} + */ + I loadCopy(); + + /** + * Loads the {@link I}'s changes to the {@link Item} instance. + */ + void update(); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ItemFactory.java b/common/src/main/java/net/momirealms/customcrops/common/item/ItemFactory.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ItemFactory.java rename to common/src/main/java/net/momirealms/customcrops/common/item/ItemFactory.java index 72c62c313..22773a33d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ItemFactory.java +++ b/common/src/main/java/net/momirealms/customcrops/common/item/ItemFactory.java @@ -1,6 +1,23 @@ -package net.momirealms.customcrops.mechanic.item.factory; - -import net.momirealms.customcrops.api.CustomCropsPlugin; +/* + * 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.common.item; + +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; import java.util.List; import java.util.Objects; @@ -35,19 +52,21 @@ public Item wrap(R item) { protected abstract I loadCopy(R item); - protected abstract Optional customModelData(R item); - protected abstract void customModelData(R item, Integer data); + protected abstract Optional customModelData(R item); + protected abstract Optional> lore(R item); protected abstract void lore(R item, List lore); - protected abstract Optional maxDamage(R item); + protected abstract Optional damage(R item); - protected abstract void maxDamage(R item, Integer data); + protected abstract void damage(R item, Integer damage); - protected abstract Optional damage(R item); + protected abstract Optional maxDamage(R item); + + protected abstract void maxDamage(R item, Integer damage); - protected abstract void damage(R item, Integer data); + protected abstract boolean unbreakable(R item); } diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionFormatter.java b/common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionFormatter.java new file mode 100644 index 000000000..49d198c02 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionFormatter.java @@ -0,0 +1,35 @@ +/* + * 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.common.locale; + +import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.caption.Caption; +import org.incendo.cloud.caption.CaptionVariable; +import org.incendo.cloud.minecraft.extras.caption.ComponentCaptionFormatter; + +import java.util.List; + +public class CustomCropsCaptionFormatter implements ComponentCaptionFormatter { + + @Override + public @NonNull Component formatCaption(@NonNull Caption captionKey, @NonNull C recipient, @NonNull String caption, @NonNull List<@NonNull CaptionVariable> variables) { + Component component = ComponentCaptionFormatter.translatable().formatCaption(captionKey, recipient, caption, variables); + return TranslationManager.render(component); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/Initable.java b/common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionKeys.java similarity index 56% rename from api/src/main/java/net/momirealms/customcrops/api/common/Initable.java rename to common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionKeys.java index 15ff32bb6..7cc8d7d9b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/common/Initable.java +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,17 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.common; +package net.momirealms.customcrops.common.locale; -public interface Initable { +import org.incendo.cloud.caption.Caption; - /** - * init - */ - void init(); +public final class CustomCropsCaptionKeys { - /** - * disable - */ - void disable(); + public static final Caption ARGUMENT_PARSE_FAILURE_TIME = Caption.of("argument.parse.failure.time"); + public static final Caption ARGUMENT_PARSE_FAILURE_URL = Caption.of("argument.parse.failure.url"); + public static final Caption ARGUMENT_PARSE_FAILURE_NAMEDTEXTCOLOR = Caption.of("argument.parse.failure.namedtextcolor"); } diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionProvider.java b/common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionProvider.java new file mode 100644 index 000000000..4be1009ce --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/CustomCropsCaptionProvider.java @@ -0,0 +1,37 @@ +/* + * 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.common.locale; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.caption.CaptionProvider; +import org.incendo.cloud.caption.DelegatingCaptionProvider; + +public final class CustomCropsCaptionProvider extends DelegatingCaptionProvider { + + private static final CaptionProvider PROVIDER = CaptionProvider.constantProvider() + .putCaption(CustomCropsCaptionKeys.ARGUMENT_PARSE_FAILURE_URL, "") + .putCaption(CustomCropsCaptionKeys.ARGUMENT_PARSE_FAILURE_TIME, "") + .putCaption(CustomCropsCaptionKeys.ARGUMENT_PARSE_FAILURE_NAMEDTEXTCOLOR, "") + .build(); + + @SuppressWarnings("unchecked") + @Override + public @NonNull CaptionProvider delegate() { + return (CaptionProvider) PROVIDER; + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java b/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java new file mode 100644 index 000000000..7305293d3 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java @@ -0,0 +1,32 @@ +/* + * 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.common.locale; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; + +public interface MessageConstants { + + TranslatableComponent.Builder COMMAND_RELOAD_SUCCESS = Component.translatable().key("command.reload.success"); + TranslatableComponent.Builder SEASON_SPRING = Component.translatable().key("season.spring"); + TranslatableComponent.Builder SEASON_SUMMER = Component.translatable().key("season.summer"); + TranslatableComponent.Builder SEASON_AUTUMN = Component.translatable().key("season.autumn"); + TranslatableComponent.Builder SEASON_WINTER = Component.translatable().key("season.winter"); + TranslatableComponent.Builder SEASON_DISABLE = Component.translatable().key("season.disable"); + +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslationRegistry.java b/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslationRegistry.java new file mode 100644 index 000000000..00072df56 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslationRegistry.java @@ -0,0 +1,73 @@ +/* + * 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.common.locale; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.translation.Translator; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +public interface MiniMessageTranslationRegistry extends Translator { + + static @NotNull MiniMessageTranslationRegistry create(final Key name, final MiniMessage miniMessage) { + return new MiniMessageTranslationRegistryImpl(requireNonNull(name, "name"), requireNonNull(miniMessage, "MiniMessage")); + } + + void register(@NotNull String key, @NotNull Locale locale, @NotNull String format); + + void unregister(@NotNull String key); + + boolean contains(@NotNull String key); + + String miniMessageTranslation(@NotNull String key, @NotNull Locale locale); + + void defaultLocale(@NotNull Locale defaultLocale); + + default void registerAll(final @NotNull Locale locale, final @NotNull Map bundle) { + this.registerAll(locale, bundle.keySet(), bundle::get); + } + + default void registerAll(final @NotNull Locale locale, final @NotNull Set keys, final Function function) { + IllegalArgumentException firstError = null; + int errorCount = 0; + for (final String key : keys) { + try { + this.register(key, locale, function.apply(key)); + } catch (final IllegalArgumentException e) { + if (firstError == null) { + firstError = e; + } + errorCount++; + } + } + if (firstError != null) { + if (errorCount == 1) { + throw firstError; + } else if (errorCount > 1) { + throw new IllegalArgumentException(String.format("Invalid key (and %d more)", errorCount - 1), firstError); + } + } + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslationRegistryImpl.java b/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslationRegistryImpl.java new file mode 100644 index 000000000..6f24359c4 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslationRegistryImpl.java @@ -0,0 +1,235 @@ +/* + * 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.common.locale; + +import net.kyori.adventure.internal.Internals; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.minimessage.Context; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.ParsingException; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.kyori.adventure.util.TriState; +import net.kyori.examination.Examinable; +import net.kyori.examination.ExaminableProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +public class MiniMessageTranslationRegistryImpl implements Examinable, MiniMessageTranslationRegistry { + private final Key name; + private final Map translations = new ConcurrentHashMap<>(); + private Locale defaultLocale = Locale.US; + private final MiniMessage miniMessage; + + MiniMessageTranslationRegistryImpl(final Key name, final MiniMessage miniMessage) { + this.name = name; + this.miniMessage = miniMessage; + } + + @Override + public void register(final @NotNull String key, final @NotNull Locale locale, final @NotNull String format) { + this.translations.computeIfAbsent(key, Translation::new).register(locale, format); + } + + @Override + public void unregister(final @NotNull String key) { + this.translations.remove(key); + } + + @Override + public boolean contains(final @NotNull String key) { + return this.translations.containsKey(key); + } + + @Override + public @NotNull Key name() { + return name; + } + + @Override + public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) { + // No need to implement this method + return null; + } + + @Override + public @Nullable Component translate(@NotNull TranslatableComponent component, @NotNull Locale locale) { + Translation translation = translations.get(component.key()); + if (translation == null) { + return null; + } + String miniMessageString = translation.translate(locale); + if (miniMessageString == null) { + return null; + } + if (miniMessageString.isEmpty()) { + return Component.empty(); + } + final Component resultingComponent; + if (component.arguments().isEmpty()) { + resultingComponent = this.miniMessage.deserialize(miniMessageString); + } else { + resultingComponent = this.miniMessage.deserialize(miniMessageString, new ArgumentTag(component.arguments())); + } + if (component.children().isEmpty()) { + return resultingComponent; + } else { + return resultingComponent.children(component.children()); + } + } + + @Override + public String miniMessageTranslation(@NotNull String key, @NotNull Locale locale) { + Translation translation = translations.get(key); + if (translation == null) { + return null; + } + return translation.translate(locale); + } + + @Override + public @NotNull TriState hasAnyTranslations() { + if (!this.translations.isEmpty()) { + return TriState.TRUE; + } + return TriState.FALSE; + } + + @Override + public void defaultLocale(final @NotNull Locale defaultLocale) { + this.defaultLocale = requireNonNull(defaultLocale, "defaultLocale"); + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of(ExaminableProperty.of("translations", this.translations)); + } + + @Override + public boolean equals(final Object other) { + if (this == other) return true; + if (!(other instanceof MiniMessageTranslationRegistryImpl that)) return false; + return this.name.equals(that.name) + && this.translations.equals(that.translations) + && this.defaultLocale.equals(that.defaultLocale); + } + + @Override + public int hashCode() { + return Objects.hash(this.name, this.translations, this.defaultLocale); + } + + @Override + public String toString() { + return Internals.toString(this); + } + + public static class ArgumentTag implements TagResolver { + private static final String NAME = "argument"; + private static final String NAME_1 = "arg"; + + private final List argumentComponents; + + public ArgumentTag(final @NotNull List argumentComponents) { + this.argumentComponents = Objects.requireNonNull(argumentComponents, "argumentComponents"); + } + + @Override + public @Nullable Tag resolve(final @NotNull String name, final @NotNull ArgumentQueue arguments, final @NotNull Context ctx) throws ParsingException { + final int index = arguments.popOr("No argument number provided").asInt().orElseThrow(() -> ctx.newException("Invalid argument number", arguments)); + + if (index < 0 || index >= argumentComponents.size()) { + throw ctx.newException("Invalid argument number", arguments); + } + + return Tag.inserting(argumentComponents.get(index)); + } + + @Override + public boolean has(final @NotNull String name) { + return name.equals(NAME) || name.equals(NAME_1); + } + } + + final class Translation implements Examinable { + private final String key; + private final Map formats; + + Translation(final @NotNull String key) { + this.key = requireNonNull(key, "translation key"); + this.formats = new ConcurrentHashMap<>(); + } + + void register(final @NotNull Locale locale, final @NotNull String format) { + if (this.formats.putIfAbsent(requireNonNull(locale, "locale"), requireNonNull(format, "message format")) != null) { + throw new IllegalArgumentException(String.format("Translation already exists: %s for %s", this.key, locale)); + } + } + + @Nullable String translate(final @NotNull Locale locale) { + String format = this.formats.get(requireNonNull(locale, "locale")); + if (format == null) { + format = this.formats.get(new Locale(locale.getLanguage())); // try without country + if (format == null) { + format = this.formats.get(MiniMessageTranslationRegistryImpl.this.defaultLocale); // try local default locale + } + } + return format; + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of( + ExaminableProperty.of("key", this.key), + ExaminableProperty.of("formats", this.formats) + ); + } + + @Override + public boolean equals(final Object other) { + if (this == other) return true; + if (!(other instanceof Translation that)) return false; + return this.key.equals(that.key) && + this.formats.equals(that.formats); + } + + @Override + public int hashCode() { + return Objects.hash(this.key, this.formats); + } + + @Override + public String toString() { + return Internals.toString(this); + } + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslator.java b/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslator.java new file mode 100644 index 000000000..6106ccf1c --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslator.java @@ -0,0 +1,47 @@ +/* + * 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.common.locale; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; +import net.kyori.adventure.translation.Translator; +import net.kyori.examination.Examinable; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; + +public interface MiniMessageTranslator extends Translator, Examinable { + + static @NotNull MiniMessageTranslator translator() { + return MiniMessageTranslatorImpl.INSTANCE; + } + + static @NotNull TranslatableComponentRenderer renderer() { + return MiniMessageTranslatorImpl.INSTANCE.renderer; + } + + static @NotNull Component render(final @NotNull Component component, final @NotNull Locale locale) { + return renderer().render(component, locale); + } + + @NotNull Iterable sources(); + + boolean addSource(final @NotNull Translator source); + + boolean removeSource(final @NotNull Translator source); +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslatorImpl.java b/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslatorImpl.java new file mode 100644 index 000000000..e566f5de7 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/MiniMessageTranslatorImpl.java @@ -0,0 +1,92 @@ +/* + * 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.common.locale; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; +import net.kyori.adventure.translation.Translator; +import net.kyori.adventure.util.TriState; +import net.kyori.examination.ExaminableProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.MessageFormat; +import java.util.Collections; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +public class MiniMessageTranslatorImpl implements MiniMessageTranslator { + + private static final Key NAME = Key.key("customcrops", "main"); + static final MiniMessageTranslatorImpl INSTANCE = new MiniMessageTranslatorImpl(); + final TranslatableComponentRenderer renderer = TranslatableComponentRenderer.usingTranslationSource(this); + private final Set sources = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + @Override + public @NotNull Key name() { + return NAME; + } + + @Override + public @NotNull TriState hasAnyTranslations() { + if (!this.sources.isEmpty()) { + return TriState.TRUE; + } + return TriState.FALSE; + } + + @Override + public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) { + // No need to implement this method + return null; + } + + @Override + public @Nullable Component translate(@NotNull TranslatableComponent component, @NotNull Locale locale) { + for (final Translator source : this.sources) { + final Component translation = source.translate(component, locale); + if (translation != null) return translation; + } + return null; + } + + @Override + public @NotNull Iterable sources() { + return Collections.unmodifiableSet(this.sources); + } + + @Override + public boolean addSource(final @NotNull Translator source) { + if (source == this) throw new IllegalArgumentException("MiniMessageTranslationSource"); + return this.sources.add(source); + } + + @Override + public boolean removeSource(final @NotNull Translator source) { + return this.sources.remove(source); + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of(ExaminableProperty.of("sources", this.sources)); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/TranslationManager.java b/common/src/main/java/net/momirealms/customcrops/common/locale/TranslationManager.java new file mode 100644 index 000000000..f385ae610 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/TranslationManager.java @@ -0,0 +1,201 @@ +/* + * 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.common.locale; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.translation.Translator; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; +import net.momirealms.customcrops.common.util.Pair; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TranslationManager { + + public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; + private static final List locales = List.of("en", "zh_cn"); + private static TranslationManager instance; + private static Locale FORCE_LOCALE = null; + + private final CustomCropsPlugin plugin; + private final Set installed = ConcurrentHashMap.newKeySet(); + private MiniMessageTranslationRegistry registry; + private final Path translationsDirectory; + + public TranslationManager(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.translationsDirectory = this.plugin.getConfigDirectory().resolve("translations"); + instance = this; + } + + public static void forceLocale(Locale locale) { + FORCE_LOCALE = locale; + } + + public void reload() { + // remove any previous registry + if (this.registry != null) { + MiniMessageTranslator.translator().removeSource(this.registry); + this.installed.clear(); + } + for (String lang : locales) { + this.plugin.getConfigManager().saveResource("translations/" + lang + ".yml"); + } + + this.registry = MiniMessageTranslationRegistry.create(Key.key("customcrops", "main"), AdventureHelper.getMiniMessage()); + this.registry.defaultLocale(DEFAULT_LOCALE); + this.loadFromFileSystem(this.translationsDirectory, false); + MiniMessageTranslator.translator().addSource(this.registry); + } + + public static String miniMessageTranslation(String key) { + return miniMessageTranslation(key, null); + } + + public static String miniMessageTranslation(String key, @Nullable Locale locale) { + if (FORCE_LOCALE != null) { + return instance.registry.miniMessageTranslation(key, FORCE_LOCALE); + } + if (locale == null) { + locale = Locale.getDefault(); + if (locale == null) { + locale = DEFAULT_LOCALE; + } + } + return instance.registry.miniMessageTranslation(key, locale); + } + + public static Component render(Component component) { + return render(component, null); + } + + public static Component render(Component component, @Nullable Locale locale) { + if (FORCE_LOCALE != null) { + return MiniMessageTranslator.render(component, FORCE_LOCALE); + } + if (locale == null) { + locale = Locale.getDefault(); + if (locale == null) { + locale = DEFAULT_LOCALE; + } + } + return MiniMessageTranslator.render(component, locale); + } + + public void loadFromFileSystem(Path directory, boolean suppressDuplicatesError) { + List translationFiles; + try (Stream stream = Files.list(directory)) { + translationFiles = stream.filter(TranslationManager::isTranslationFile).collect(Collectors.toList()); + } catch (IOException e) { + translationFiles = Collections.emptyList(); + } + + if (translationFiles.isEmpty()) { + return; + } + + Map> loaded = new HashMap<>(); + for (Path translationFile : translationFiles) { + try { + Pair> result = loadTranslationFile(translationFile); + loaded.put(result.left(), result.right()); + } catch (Exception e) { + if (!suppressDuplicatesError || !isAdventureDuplicatesException(e)) { + this.plugin.getPluginLogger().warn("Error loading locale file: " + translationFile.getFileName(), e); + } + } + } + + // try registering the locale without a country code - if we don't already have a registration for that + loaded.forEach((locale, bundle) -> { + Locale localeWithoutCountry = new Locale(locale.getLanguage()); + if (!locale.equals(localeWithoutCountry) && !localeWithoutCountry.equals(DEFAULT_LOCALE) && this.installed.add(localeWithoutCountry)) { + try { + this.registry.registerAll(localeWithoutCountry, bundle); + } catch (IllegalArgumentException e) { + // ignore + } + } + }); + + Locale localLocale = Locale.getDefault(); + if (!this.installed.contains(localLocale)) { + plugin.getPluginLogger().warn(localLocale.toString().toLowerCase(Locale.ENGLISH) + ".yml not exists, using en.yml as default locale."); + } + } + + public static boolean isTranslationFile(Path path) { + return path.getFileName().toString().endsWith(".yml"); + } + + private static boolean isAdventureDuplicatesException(Exception e) { + return e instanceof IllegalArgumentException && (e.getMessage().startsWith("Invalid key") || e.getMessage().startsWith("Translation already exists")); + } + + @SuppressWarnings("unchecked") + private Pair> loadTranslationFile(Path translationFile) { + String fileName = translationFile.getFileName().toString(); + String localeString = fileName.substring(0, fileName.length() - ".yml".length()); + Locale locale = parseLocale(localeString); + + if (locale == null) { + throw new IllegalStateException("Unknown locale '" + localeString + "' - unable to register."); + } + + Map bundle = new HashMap<>(); + YamlDocument document = plugin.getConfigManager().loadConfig("translations" + "\\" + translationFile.getFileName(), '@'); + try { + document.save(new File(plugin.getDataDirectory().toFile(), "translations" + "\\" + translationFile.getFileName())); + } catch (IOException e) { + throw new IllegalStateException("Could not update translation file: " + translationFile.getFileName(), e); + } + Map map = document.getStringRouteMappedValues(false); + map.remove("config-version"); + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() instanceof List list) { + List strList = (List) list; + StringJoiner stringJoiner = new StringJoiner(""); + for (String str : strList) { + stringJoiner.add(str); + } + bundle.put(entry.getKey(), stringJoiner.toString()); + } else if (entry.getValue() instanceof String str) { + bundle.put(entry.getKey(), str); + } + } + + this.registry.registerAll(locale, bundle); + this.installed.add(locale); + + return Pair.of(locale, bundle); + } + + public static @Nullable Locale parseLocale(@Nullable String locale) { + return locale == null || locale.isEmpty() ? null : Translator.parseLocale(locale); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/plugin/CustomCropsPlugin.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/CustomCropsPlugin.java new file mode 100644 index 000000000..77eeb2432 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/CustomCropsPlugin.java @@ -0,0 +1,138 @@ +/* + * 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.common.plugin; + +import net.momirealms.customcrops.common.config.ConfigLoader; +import net.momirealms.customcrops.common.dependency.DependencyManager; +import net.momirealms.customcrops.common.locale.TranslationManager; +import net.momirealms.customcrops.common.plugin.classpath.ClassPathAppender; +import net.momirealms.customcrops.common.plugin.logging.PluginLogger; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerAdapter; + +import java.io.InputStream; +import java.nio.file.Path; + +/** + * Interface representing the main CustomCrops plugin. + */ +public interface CustomCropsPlugin { + + /** + * Retrieves an input stream for a resource file within the plugin. + * + * @param filePath the path to the resource file + * @return an {@link InputStream} for the resource file + */ + InputStream getResourceStream(String filePath); + + /** + * Retrieves the plugin logger. + * + * @return the {@link PluginLogger} instance + */ + PluginLogger getPluginLogger(); + + /** + * Retrieves the class path appender. + * + * @return the {@link ClassPathAppender} instance + */ + ClassPathAppender getClassPathAppender(); + + /** + * Retrieves the scheduler adapter. + * + * @return the {@link SchedulerAdapter} instance + */ + SchedulerAdapter getScheduler(); + + /** + * Retrieves the data directory path. + * + * @return the {@link Path} to the data directory + */ + Path getDataDirectory(); + + /** + * Retrieves the configuration directory path. + * By default, this is the same as the data directory. + * + * @return the {@link Path} to the configuration directory + */ + default Path getConfigDirectory() { + return getDataDirectory(); + } + + /** + * Retrieves the dependency manager. + * + * @return the {@link DependencyManager} instance + */ + DependencyManager getDependencyManager(); + + /** + * Retrieves the translation manager. + * + * @return the {@link TranslationManager} instance + */ + TranslationManager getTranslationManager(); + + /** + * Retrieves the configuration manager. + * + * @return the {@link ConfigLoader} instance + */ + ConfigLoader getConfigManager(); + + /** + * Retrieves the server version. + * + * @return the server version as a string + */ + String getServerVersion(); + + /** + * Retrieves the plugin version. + * + * @return the plugin version as a string + */ + String getPluginVersion(); + + /** + * Loads the plugin. + * This method is called during the plugin's loading phase. + */ + void load(); + + /** + * Enables the plugin. + * This method is called during the plugin's enabling phase. + */ + void enable(); + + /** + * Disables the plugin. + * This method is called during the plugin's disabling phase. + */ + void disable(); + + /** + * Reloads the plugin. + */ + void reload(); +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/plugin/CustomCropsProperties.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/CustomCropsProperties.java new file mode 100644 index 000000000..a0d0bde62 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/CustomCropsProperties.java @@ -0,0 +1,61 @@ +/* + * 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.common.plugin; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class CustomCropsProperties { + + private final HashMap propertyMap; + + private CustomCropsProperties(HashMap propertyMap) { + this.propertyMap = propertyMap; + } + + public static String getValue(String key) { + if (!SingletonHolder.INSTANCE.propertyMap.containsKey(key)) { + throw new RuntimeException("Unknown key: " + key); + } + return SingletonHolder.INSTANCE.propertyMap.get(key); + } + + private static class SingletonHolder { + + private static final CustomCropsProperties INSTANCE = getInstance(); + + private static CustomCropsProperties getInstance() { + try (InputStream inputStream = CustomCropsProperties.class.getClassLoader().getResourceAsStream("custom-crops.properties")) { + HashMap versionMap = new HashMap<>(); + Properties properties = new Properties(); + properties.load(inputStream); + for (Map.Entry entry : properties.entrySet()) { + if (entry.getKey() instanceof String key && entry.getValue() instanceof String value) { + versionMap.put(key, value); + } + } + return new CustomCropsProperties(versionMap); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ClassPathAppender.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/ClassPathAppender.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ClassPathAppender.java rename to common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/ClassPathAppender.java index d63541ffb..42abf376b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/ClassPathAppender.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.classpath; +package net.momirealms.customcrops.common.plugin.classpath; import java.nio.file.Path; diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ReflectionClassPathAppender.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/ReflectionClassPathAppender.java similarity index 88% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ReflectionClassPathAppender.java rename to common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/ReflectionClassPathAppender.java index 101c50e7c..052f6d893 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ReflectionClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/ReflectionClassPathAppender.java @@ -23,7 +23,9 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.classpath; +package net.momirealms.customcrops.common.plugin.classpath; + +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; import java.net.MalformedURLException; import java.net.URLClassLoader; @@ -40,6 +42,10 @@ public ReflectionClassPathAppender(ClassLoader classLoader) throws IllegalStateE } } + public ReflectionClassPathAppender(CustomCropsPlugin plugin) throws IllegalStateException { + this(plugin.getClass().getClassLoader()); + } + @Override public void addJarToClasspath(Path file) { try { diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/URLClassLoaderAccess.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/URLClassLoaderAccess.java similarity index 95% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/URLClassLoaderAccess.java rename to common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/URLClassLoaderAccess.java index 1a151d474..dbc41c64a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/URLClassLoaderAccess.java +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/classpath/URLClassLoaderAccess.java @@ -23,9 +23,9 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.classpath; +package net.momirealms.customcrops.common.plugin.classpath; -import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -66,7 +66,7 @@ protected URLClassLoaderAccess(URLClassLoader classLoader) { * * @param url the URL to add */ - public abstract void addURL(@NonNull URL url); + public abstract void addURL(@NotNull URL url); private static void throwError(Throwable cause) throws UnsupportedOperationException { throw new UnsupportedOperationException("CustomCrops is unable to inject into the plugin URLClassLoader.\n" + @@ -100,7 +100,7 @@ private static boolean isSupported() { } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { try { ADD_URL_METHOD.invoke(super.classLoader, url); } catch (ReflectiveOperationException e) { @@ -162,7 +162,7 @@ private static Object fetchField(final Class clazz, final Object object, fina } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { if (this.unopenedURLs == null || this.pathURLs == null) { URLClassLoaderAccess.throwError(new NullPointerException("unopenedURLs or pathURLs")); } @@ -182,7 +182,7 @@ private Noop() { } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { URLClassLoaderAccess.throwError(null); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/Reloadable.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/feature/Reloadable.java similarity index 76% rename from api/src/main/java/net/momirealms/customcrops/api/common/Reloadable.java rename to common/src/main/java/net/momirealms/customcrops/common/plugin/feature/Reloadable.java index c19bc59ae..485d7b2c3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/common/Reloadable.java +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/feature/Reloadable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,26 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.common; +package net.momirealms.customcrops.common.plugin.feature; -public interface Reloadable extends Initable { +public interface Reloadable { - @Override - default void init() { + default void reload() { + unload(); load(); } - void load(); - - void unload(); + default void unload() { + } - @Override - default void disable() { - unload(); + default void load() { } - default void reload() { + default void disable() { unload(); - load(); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/JarInJarClassPathAppender.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/JavaPluginLogger.java similarity index 54% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/JarInJarClassPathAppender.java rename to common/src/main/java/net/momirealms/customcrops/common/plugin/logging/JavaPluginLogger.java index 13b2d4051..559d934c0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/JarInJarClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/JavaPluginLogger.java @@ -23,40 +23,40 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.classpath; +package net.momirealms.customcrops.common.plugin.logging; -import net.momirealms.customcrops.libraries.loader.JarInJarClassLoader; +import java.util.logging.Level; +import java.util.logging.Logger; -import java.io.IOException; -import java.net.MalformedURLException; -import java.nio.file.Path; +public class JavaPluginLogger implements PluginLogger { + private final Logger logger; -public class JarInJarClassPathAppender implements ClassPathAppender { - private final JarInJarClassLoader classLoader; + public JavaPluginLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void info(String s) { + this.logger.info(s); + } - public JarInJarClassPathAppender(ClassLoader classLoader) { - if (!(classLoader instanceof JarInJarClassLoader)) { - throw new IllegalArgumentException("Loader is not a JarInJarClassLoader: " + classLoader.getClass().getName()); - } - this.classLoader = (JarInJarClassLoader) classLoader; + @Override + public void warn(String s) { + this.logger.warning(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.log(Level.WARNING, s, t); } @Override - public void addJarToClasspath(Path file) { - try { - this.classLoader.addJarToClasspath(file.toUri().toURL()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } + public void severe(String s) { + this.logger.severe(s); } @Override - public void close() { - this.classLoader.deleteJarResource(); - try { - this.classLoader.close(); - } catch (IOException e) { - e.printStackTrace(); - } + public void severe(String s, Throwable t) { + this.logger.log(Level.SEVERE, s, t); } } diff --git a/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/Log4jPluginLogger.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/Log4jPluginLogger.java new file mode 100644 index 000000000..8bf8efe96 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/Log4jPluginLogger.java @@ -0,0 +1,61 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.plugin.logging; + +import org.apache.logging.log4j.Logger; + +public class Log4jPluginLogger implements PluginLogger { + private final Logger logger; + + public Log4jPluginLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void info(String s) { + this.logger.info(s); + } + + @Override + public void warn(String s) { + this.logger.warn(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.warn(s, t); + } + + @Override + public void severe(String s) { + this.logger.error(s); + } + + @Override + public void severe(String s, Throwable t) { + this.logger.error(s, t); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoadingException.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/PluginLogger.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoadingException.java rename to common/src/main/java/net/momirealms/customcrops/common/plugin/logging/PluginLogger.java index 2d5e92533..0c5f292a8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoadingException.java +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/PluginLogger.java @@ -23,19 +23,24 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.loader; +package net.momirealms.customcrops.common.plugin.logging; /** - * Runtime exception used if there is a problem during loading + * Represents the logger instance being used by CustomCrops on the platform. + * + *

Messages sent using the logger are sent prefixed with the CustomCrops tag, + * and on some implementations will be colored depending on the message type.

*/ -public class LoadingException extends RuntimeException { +public interface PluginLogger { + + void info(String s); + + void warn(String s); + + void warn(String s, Throwable t); - public LoadingException(String message) { - super(message); - } + void severe(String s); - public LoadingException(String message, Throwable cause) { - super(message, cause); - } + void severe(String s, Throwable t); } diff --git a/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/Slf4jPluginLogger.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/Slf4jPluginLogger.java new file mode 100644 index 000000000..2be9f80bd --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/logging/Slf4jPluginLogger.java @@ -0,0 +1,61 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.plugin.logging; + +import org.slf4j.Logger; + +public class Slf4jPluginLogger implements PluginLogger { + private final Logger logger; + + public Slf4jPluginLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void info(String s) { + this.logger.info(s); + } + + @Override + public void warn(String s) { + this.logger.warn(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.warn(s, t); + } + + @Override + public void severe(String s) { + this.logger.error(s); + } + + @Override + public void severe(String s, Throwable t) { + this.logger.error(s, t); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/AbstractJavaScheduler.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/AbstractJavaScheduler.java new file mode 100644 index 000000000..0c0561fbf --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/AbstractJavaScheduler.java @@ -0,0 +1,151 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.plugin.scheduler; + +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Arrays; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Abstract implementation of {@link SchedulerAdapter} using a {@link ScheduledExecutorService}. + */ +public abstract class AbstractJavaScheduler implements SchedulerAdapter { + private static final int PARALLELISM = 16; + + private final CustomCropsPlugin plugin; + + private final ScheduledThreadPoolExecutor scheduler; + private final ForkJoinPool worker; + + public AbstractJavaScheduler(CustomCropsPlugin plugin) { + this.plugin = plugin; + + this.scheduler = new ScheduledThreadPoolExecutor(4, r -> { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName("customcrops-scheduler"); + return thread; + }); + this.scheduler.setRemoveOnCancelPolicy(true); + this.scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.worker = new ForkJoinPool(PARALLELISM, new WorkerThreadFactory(), new ExceptionHandler(), false); + } + + @Override + public Executor async() { + return this.worker; + } + + @Override + public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) { + ScheduledFuture future = this.scheduler.schedule(() -> this.worker.execute(task), delay, unit); + return new JavaCancellable(future); + } + + @Override + public SchedulerTask asyncRepeating(Runnable task, long delay, long interval, TimeUnit unit) { + ScheduledFuture future = this.scheduler.scheduleAtFixedRate(() -> this.worker.execute(task), delay, interval, unit); + return new JavaCancellable(future); + } + + @Override + public void shutdownScheduler() { + this.scheduler.shutdown(); + try { + if (!this.scheduler.awaitTermination(1, TimeUnit.MINUTES)) { + this.plugin.getPluginLogger().severe("Timed out waiting for the CustomCrops scheduler to terminate"); + reportRunningTasks(thread -> thread.getName().equals("customcrops-scheduler")); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Override + public void shutdownExecutor() { + this.worker.shutdown(); + try { + if (!this.worker.awaitTermination(1, TimeUnit.MINUTES)) { + this.plugin.getPluginLogger().severe("Timed out waiting for the CustomCrops worker thread pool to terminate"); + reportRunningTasks(thread -> thread.getName().startsWith("customcrops-worker-")); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void reportRunningTasks(Predicate predicate) { + Thread.getAllStackTraces().forEach((thread, stack) -> { + if (predicate.test(thread)) { + this.plugin.getPluginLogger().warn("Thread " + thread.getName() + " is blocked, and may be the reason for the slow shutdown!\n" + + Arrays.stream(stack).map(el -> " " + el).collect(Collectors.joining("\n")) + ); + } + }); + } + + private static final class WorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + private static final AtomicInteger COUNT = new AtomicInteger(0); + + @Override + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setDaemon(true); + thread.setName("customcrops-worker-" + COUNT.getAndIncrement()); + return thread; + } + } + + private final class ExceptionHandler implements UncaughtExceptionHandler { + @Override + public void uncaughtException(Thread t, Throwable e) { + AbstractJavaScheduler.this.plugin.getPluginLogger().warn("Thread " + t.getName() + " threw an uncaught exception", e); + } + } + + public static class JavaCancellable implements SchedulerTask { + + private final ScheduledFuture future; + + public JavaCancellable(ScheduledFuture future) { + this.future = future; + } + + @Override + public void cancel() { + this.future.cancel(false); + } + + @Override + public boolean isCancelled() { + return future.isCancelled(); + } + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/RegionExecutor.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/RegionExecutor.java new file mode 100644 index 000000000..3de0b65c8 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/RegionExecutor.java @@ -0,0 +1,33 @@ +/* + * 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.common.plugin.scheduler; + +public interface RegionExecutor { + + void run(Runnable r, T l); + + default void run(Runnable r) { + run(r, null); + } + + void run(Runnable r, W world, int x, int z); + + SchedulerTask runLater(Runnable r, long delayTicks, T l); + + SchedulerTask runRepeating(Runnable r, long delayTicks, long period, T l); +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/SchedulerAdapter.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/SchedulerAdapter.java new file mode 100644 index 000000000..1903364e6 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/SchedulerAdapter.java @@ -0,0 +1,111 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.plugin.scheduler; + +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +/** + * A scheduler for running tasks using the systems provided by the platform + */ +public interface SchedulerAdapter { + + /** + * Gets an async executor instance + * + * @return an async executor instance + */ + Executor async(); + + /** + * Gets a sync executor instance + * + * @return a sync executor instance + */ + RegionExecutor sync(); + + /** + * Executes a task async + * + * @param task the task + */ + default void executeAsync(Runnable task) { + async().execute(task); + } + + /** + * Executes a task sync + * + * @param task the task + */ + default void executeSync(Runnable task, T location) { + sync().run(task, location); + } + + /** + * Executes a task sync + * + * @param task the task + */ + default void executeSync(Runnable task) { + sync().run(task, null); + } + + /** + * Executes the given task with a delay. + * + * @param task the task + * @param delay the delay + * @param unit the unit of delay + * @return the resultant task instance + */ + SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit); + + /** + * Executes the given task repeatedly at a given interval. + * + * @param task the task + * @param interval the interval + * @param unit the unit of interval + * @return the resultant task instance + */ + SchedulerTask asyncRepeating(Runnable task, long delay, long interval, TimeUnit unit); + + /** + * Shuts down the scheduler instance. + * + *

{@link #asyncLater(Runnable, long, TimeUnit)} and {@link #asyncRepeating(Runnable, long, long, TimeUnit)}.

+ */ + void shutdownScheduler(); + + /** + * Shuts down the executor instance. + * + *

{@link #async()} and {@link #executeAsync(Runnable)}.

+ */ + void shutdownExecutor(); + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoaderBootstrap.java b/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/SchedulerTask.java similarity index 84% rename from plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoaderBootstrap.java rename to common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/SchedulerTask.java index fbdd07fcd..43a11a920 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoaderBootstrap.java +++ b/common/src/main/java/net/momirealms/customcrops/common/plugin/scheduler/SchedulerTask.java @@ -23,17 +23,17 @@ * SOFTWARE. */ -package net.momirealms.customcrops.libraries.loader; +package net.momirealms.customcrops.common.plugin.scheduler; /** - * Minimal bootstrap plugin, called by the loader plugin. + * Represents a scheduled task */ -public interface LoaderBootstrap { +public interface SchedulerTask { - void onLoad(); - - default void onEnable() {} - - default void onDisable() {} + /** + * Cancels the task. + */ + void cancel(); + boolean isCancelled(); } diff --git a/common/src/main/java/net/momirealms/customcrops/common/sender/AbstractSender.java b/common/src/main/java/net/momirealms/customcrops/common/sender/AbstractSender.java new file mode 100644 index 000000000..e44f53cfa --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/sender/AbstractSender.java @@ -0,0 +1,116 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.sender; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; +import net.momirealms.customcrops.common.util.Tristate; + +import java.util.UUID; + +/** + * Simple implementation of {@link Sender} using a {@link SenderFactory} + * + * @param the command sender type + */ +public final class AbstractSender implements Sender { + private final CustomCropsPlugin plugin; + private final SenderFactory factory; + private final T sender; + + private final UUID uniqueId; + private final String name; + private final boolean isConsole; + + AbstractSender(CustomCropsPlugin plugin, SenderFactory factory, T sender) { + this.plugin = plugin; + this.factory = factory; + this.sender = sender; + this.uniqueId = factory.getUniqueId(this.sender); + this.name = factory.getName(this.sender); + this.isConsole = this.factory.isConsole(this.sender); + } + + @Override + public CustomCropsPlugin getPlugin() { + return this.plugin; + } + + @Override + public UUID getUniqueId() { + return this.uniqueId; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void sendMessage(Component message) { + this.factory.sendMessage(this.sender, message); + } + + @Override + public void sendMessage(Component message, boolean ignoreEmpty) { + if (ignoreEmpty && message.equals(Component.empty())) { + return; + } + sendMessage(message); + } + + @Override + public Tristate getPermissionValue(String permission) { + return (isConsole() && this.factory.consoleHasAllPermissions()) ? Tristate.TRUE : this.factory.getPermissionValue(this.sender, permission); + } + + @Override + public boolean hasPermission(String permission) { + return (isConsole() && this.factory.consoleHasAllPermissions()) || this.factory.hasPermission(this.sender, permission); + } + + @Override + public void performCommand(String commandLine) { + this.factory.performCommand(this.sender, commandLine); + } + + @Override + public boolean isConsole() { + return this.isConsole; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof AbstractSender that)) return false; + return this.getUniqueId().equals(that.getUniqueId()); + } + + @Override + public int hashCode() { + return this.uniqueId.hashCode(); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/sender/DummyConsoleSender.java b/common/src/main/java/net/momirealms/customcrops/common/sender/DummyConsoleSender.java new file mode 100644 index 000000000..18f7a2707 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/sender/DummyConsoleSender.java @@ -0,0 +1,62 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.sender; + +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; + +import java.util.UUID; + +public abstract class DummyConsoleSender implements Sender { + private final CustomCropsPlugin platform; + + public DummyConsoleSender(CustomCropsPlugin plugin) { + this.platform = plugin; + } + + @Override + public void performCommand(String commandLine) { + } + + @Override + public boolean isConsole() { + return true; + } + + @Override + public CustomCropsPlugin getPlugin() { + return this.platform; + } + + @Override + public UUID getUniqueId() { + return Sender.CONSOLE_UUID; + } + + @Override + public String getName() { + return Sender.CONSOLE_NAME; + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/sender/Sender.java b/common/src/main/java/net/momirealms/customcrops/common/sender/Sender.java new file mode 100644 index 000000000..90576b036 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/sender/Sender.java @@ -0,0 +1,121 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.sender; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; +import net.momirealms.customcrops.common.util.Tristate; + +import java.util.UUID; + +/** + * Wrapper interface to represent a CommandSender/CommandSource within the common command implementations. + */ +public interface Sender { + + /** The uuid used by the console sender. */ + UUID CONSOLE_UUID = new UUID(0, 0); // 00000000-0000-0000-0000-000000000000 + /** The name used by the console sender. */ + String CONSOLE_NAME = "Console"; + + /** + * Gets the plugin instance the sender is from. + * + * @return the plugin + */ + CustomCropsPlugin getPlugin(); + + /** + * Gets the sender's username + * + * @return a friendly username for the sender + */ + String getName(); + + /** + * Gets the sender's unique id. + * + *

See {@link #CONSOLE_UUID} for the console's UUID representation.

+ * + * @return the sender's uuid + */ + UUID getUniqueId(); + + /** + * Send a json message to the Sender. + * + * @param message the message to send. + */ + void sendMessage(Component message); + + /** + * Send a json message to the Sender. + * + * @param message the message to send. + * @param ignoreEmpty whether to ignore empty component + */ + void sendMessage(Component message, boolean ignoreEmpty); + + /** + * Gets the tristate a permission is set to. + * + * @param permission the permission to check for + * @return a tristate + */ + Tristate getPermissionValue(String permission); + + /** + * Check if the Sender has a permission. + * + * @param permission the permission to check for + * @return true if the sender has the permission + */ + boolean hasPermission(String permission); + + /** + * Makes the sender perform a command. + * + * @param commandLine the command + */ + void performCommand(String commandLine); + + /** + * Gets whether this sender is the console + * + * @return if the sender is the console + */ + boolean isConsole(); + + /** + * Gets whether this sender is still valid & receiving messages. + * + * @return if this sender is valid + */ + default boolean isValid() { + return true; + } + +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/sender/SenderFactory.java b/common/src/main/java/net/momirealms/customcrops/common/sender/SenderFactory.java new file mode 100644 index 000000000..cca7dc693 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/sender/SenderFactory.java @@ -0,0 +1,82 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.sender; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; +import net.momirealms.customcrops.common.util.Tristate; + +import java.util.Objects; +import java.util.UUID; + +/** + * Factory class to make a thread-safe sender instance + * + * @param

the plugin type + * @param the command sender type + */ +public abstract class SenderFactory

implements AutoCloseable { + private final P plugin; + + public SenderFactory(P plugin) { + this.plugin = plugin; + } + + protected P getPlugin() { + return this.plugin; + } + + protected abstract UUID getUniqueId(T sender); + + protected abstract String getName(T sender); + + public abstract Audience getAudience(T sender); + + protected abstract void sendMessage(T sender, Component message); + + protected abstract Tristate getPermissionValue(T sender, String node); + + protected abstract boolean hasPermission(T sender, String node); + + protected abstract void performCommand(T sender, String command); + + protected abstract boolean isConsole(T sender); + + protected boolean consoleHasAllPermissions() { + return true; + } + + public final Sender wrap(T sender) { + Objects.requireNonNull(sender, "sender"); + return new AbstractSender<>(this.plugin, this, sender); + } + + @Override + public void close() { + + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/ArrayUtils.java b/common/src/main/java/net/momirealms/customcrops/common/util/ArrayUtils.java new file mode 100644 index 000000000..da7798a86 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/ArrayUtils.java @@ -0,0 +1,102 @@ +/* + * 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.common.util; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Utility class for handling operations with arrays. + */ +public class ArrayUtils { + + private ArrayUtils() {} + + /** + * Creates a subarray from the specified array starting from the given index. + * + * @param array the original array + * @param index the starting index for the subarray + * @param the type of the elements in the array + * @return the subarray starting from the given index + * @throws IllegalArgumentException if the index is less than 0 + */ + public static T[] subArray(T[] array, int index) { + if (index < 0) { + throw new IllegalArgumentException("Index should be a value no lower than 0"); + } + if (array.length <= index) { + @SuppressWarnings("unchecked") + T[] emptyArray = (T[]) Array.newInstance(array.getClass().getComponentType(), 0); + return emptyArray; + } + @SuppressWarnings("unchecked") + T[] subArray = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length - index); + System.arraycopy(array, index, subArray, 0, array.length - index); + return subArray; + } + + /** + * Splits the specified array into a list of subarrays, each with the specified chunk size. + * + * @param array the original array + * @param chunkSize the size of each chunk + * @param the type of the elements in the array + * @return a list of subarrays + */ + public static List splitArray(T[] array, int chunkSize) { + List result = new ArrayList<>(); + for (int i = 0; i < array.length; i += chunkSize) { + int end = Math.min(array.length, i + chunkSize); + @SuppressWarnings("unchecked") + T[] chunk = (T[]) Array.newInstance(array.getClass().getComponentType(), end - i); + System.arraycopy(array, i, chunk, 0, end - i); + result.add(chunk); + } + return result; + } + + /** + * Appends an element to the specified array. + * + * @param array the original array + * @param element the element to append + * @param the type of the elements in the array + * @return a new array with the appended element + */ + public static T[] appendElementToArray(T[] array, T element) { + T[] newArray = Arrays.copyOf(array, array.length + 1); + newArray[array.length] = element; + return newArray; + } + + /** + * Splits a string value into an array of substrings based on comma separation. + * The input string is expected to be in the format "[value1, value2, ...]". + * + * @param value the string value to split + * @return an array of substrings + */ + public static String[] splitValue(String value) { + return value.substring(value.indexOf('[') + 1, value.lastIndexOf(']')) + .replaceAll("\\s", "") + .split(","); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ClassUtils.java b/common/src/main/java/net/momirealms/customcrops/common/util/ClassUtils.java similarity index 50% rename from plugin/src/main/java/net/momirealms/customcrops/util/ClassUtils.java rename to common/src/main/java/net/momirealms/customcrops/common/util/ClassUtils.java index b558e6b44..9737c5c40 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ClassUtils.java +++ b/common/src/main/java/net/momirealms/customcrops/common/util/ClassUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,13 +15,15 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.util; +package net.momirealms.customcrops.common.util; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -29,58 +31,58 @@ import java.util.jar.JarEntry; import java.util.jar.JarInputStream; +/** + * Utility class for handling classes. + */ public class ClassUtils { private ClassUtils() {} - /** - * Attempts to find a class within a JAR file that extends or implements a given class or interface. - * - * @param file The JAR file in which to search for the class. - * @param clazz The base class or interface to match against. - * @param The type of the base class or interface. - * @return A Class object representing the found class, or null if not found. - * @throws IOException If there is an issue reading the JAR file. - * @throws ClassNotFoundException If the specified class cannot be found. - */ @Nullable - public static Class findClass( + public static Class findClass( @NotNull File file, - @NotNull Class clazz + @NotNull Class clazz, + @NotNull Class type ) throws IOException, ClassNotFoundException { if (!file.exists()) { return null; } - URL jar = file.toURI().toURL(); - URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); - List matches = new ArrayList<>(); + URL jarUrl = file.toURI().toURL(); List> classes = new ArrayList<>(); - try (JarInputStream stream = new JarInputStream(jar.openStream())) { + try (URLClassLoader loader = new URLClassLoader(new URL[]{jarUrl}, clazz.getClassLoader()); + JarInputStream jarStream = new JarInputStream(jarUrl.openStream())) { + JarEntry entry; - while ((entry = stream.getNextJarEntry()) != null) { - final String name = entry.getName(); + while ((entry = jarStream.getNextJarEntry()) != null) { + String name = entry.getName(); if (!name.endsWith(".class")) { continue; } - matches.add(name.substring(0, name.lastIndexOf('.')).replace('/', '.')); - } - for (String match : matches) { + String className = name.substring(0, name.lastIndexOf('.')).replace('/', '.'); + try { - Class loaded = loader.loadClass(match); - if (clazz.isAssignableFrom(loaded)) { - classes.add(loaded.asSubclass(clazz)); + Class loadedClass = loader.loadClass(className); + if (clazz.isAssignableFrom(loadedClass)) { + Type superclassType = loadedClass.getGenericSuperclass(); + if (superclassType instanceof ParameterizedType parameterizedType) { + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + if (typeArguments.length > 0 && typeArguments[0].equals(type)) { + classes.add(loadedClass.asSubclass(clazz)); + } + } } - } catch (NoClassDefFoundError ignored) { + } catch (ClassNotFoundException | NoClassDefFoundError ignored) { } } } + if (classes.isEmpty()) { - loader.close(); return null; } + return classes.get(0); } } diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/CompletableFutures.java b/common/src/main/java/net/momirealms/customcrops/common/util/CompletableFutures.java new file mode 100644 index 000000000..7e6b99c69 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/CompletableFutures.java @@ -0,0 +1,73 @@ +/* + * 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.common.util; + +import com.google.common.collect.ImmutableList; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collector; +import java.util.stream.Stream; + +/** + * Utility class for handling operations with {@link CompletableFuture}. + */ +public class CompletableFutures { + + private CompletableFutures() {} + + /** + * A collector for collecting a stream of CompletableFuture instances into a single CompletableFuture that completes + * when all of the input CompletableFutures complete. + * + * @param The type of CompletableFuture. + * @return A collector for CompletableFuture instances. + */ + public static > Collector, CompletableFuture> collector() { + return Collector.of( + ImmutableList.Builder::new, + ImmutableList.Builder::add, + (l, r) -> l.addAll(r.build()), + builder -> allOf(builder.build()) + ); + } + + /** + * Combines multiple CompletableFuture instances into a single CompletableFuture that completes when all of the input + * CompletableFutures complete. + * + * @param futures A stream of CompletableFuture instances. + * @return A CompletableFuture that completes when all input CompletableFutures complete. + */ + public static CompletableFuture allOf(Stream> futures) { + CompletableFuture[] arr = futures.toArray(CompletableFuture[]::new); + return CompletableFuture.allOf(arr); + } + + /** + * Combines multiple CompletableFuture instances into a single CompletableFuture that completes when all of the input + * CompletableFutures complete. + * + * @param futures A collection of CompletableFuture instances. + * @return A CompletableFuture that completes when all input CompletableFutures complete. + */ + public static CompletableFuture allOf(Collection> futures) { + CompletableFuture[] arr = futures.toArray(new CompletableFuture[0]); + return CompletableFuture.allOf(arr); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/Either.java b/common/src/main/java/net/momirealms/customcrops/common/util/Either.java new file mode 100644 index 000000000..bb1809d83 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/Either.java @@ -0,0 +1,113 @@ +/* + * 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.common.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +/** + * Interface representing a value that can be either of two types, primary or fallback. + * Provides methods to create instances of either type and to map between them. + * + * @param the type of the primary value + * @param the type of the fallback value + */ +public interface Either { + + /** + * Creates an {@link Either} instance with a primary value. + * + * @param value the primary value + * @param the type of the primary value + * @param the type of the fallback value + * @return an {@link Either} instance with the primary value + */ + static @NotNull Either ofPrimary(final @NotNull U value) { + return EitherImpl.of(requireNonNull(value, "value"), null); + } + + /** + * Creates an {@link Either} instance with a fallback value. + * + * @param value the fallback value + * @param the type of the primary value + * @param the type of the fallback value + * @return an {@link Either} instance with the fallback value + */ + static @NotNull Either ofFallback(final @NotNull V value) { + return EitherImpl.of(null, requireNonNull(value, "value")); + } + + /** + * Retrieves the primary value, if present. + * + * @return an {@link Optional} containing the primary value, or empty if not present + */ + @NotNull + Optional primary(); + + /** + * Retrieves the fallback value, if present. + * + * @return an {@link Optional} containing the fallback value, or empty if not present + */ + @NotNull + Optional fallback(); + + /** + * Retrieves the primary value, or maps the fallback value to the primary type if the primary is not present. + * + * @param mapFallback a function to map the fallback value to the primary type + * @return the primary value, or the mapped fallback value if the primary is not present + */ + default @Nullable U primaryOrMapFallback(final @NotNull Function mapFallback) { + return this.primary().orElseGet(() -> mapFallback.apply(this.fallback().get())); + } + + /** + * Retrieves the fallback value, or maps the primary value to the fallback type if the fallback is not present. + * + * @param mapPrimary a function to map the primary value to the fallback type + * @return the fallback value, or the mapped primary value if the fallback is not present + */ + default @Nullable V fallbackOrMapPrimary(final @NotNull Function mapPrimary) { + return this.fallback().orElseGet(() -> mapPrimary.apply(this.primary().get())); + } + + /** + * Maps either the primary or fallback value to a new type. + * + * @param mapPrimary a function to map the primary value + * @param mapFallback a function to map the fallback value + * @param the type of the result + * @return the mapped result + */ + default @NotNull R mapEither( + final @NotNull Function mapPrimary, + final @NotNull Function mapFallback + ) { + return this.primary() + .map(mapPrimary) + .orElseGet(() -> this.fallback().map(mapFallback).get()); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/EitherImpl.java b/common/src/main/java/net/momirealms/customcrops/common/util/EitherImpl.java new file mode 100644 index 000000000..8e2c7f7c0 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/EitherImpl.java @@ -0,0 +1,130 @@ +/* + * 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.common.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; + +final class EitherImpl implements Either { + private final @Nullable U primary; + private final @Nullable V fallback; + + private EitherImpl(Optional primary, Optional fallback) { + this.primary = primary.orElse(null); + this.fallback = fallback.orElse(null); + } + + private EitherImpl(@Nullable U primary, @Nullable V fallback) { + this.primary = primary; + this.fallback = fallback; + } + + private EitherImpl( + EitherImpl original, + @Nullable U primary, + @Nullable V fallback + ) { + this.primary = primary; + this.fallback = fallback; + } + + @Override + public @NotNull Optional primary() { + return Optional.ofNullable(primary); + } + + @Override + public @NotNull Optional fallback() { + return Optional.ofNullable(fallback); + } + + public final EitherImpl withPrimary(@Nullable U value) { + @Nullable U newValue = value; + if (this.primary == newValue) return this; + return new EitherImpl<>(this, newValue, this.fallback); + } + + public EitherImpl withPrimary(Optional optional) { + @Nullable U value = optional.orElse(null); + if (this.primary == value) return this; + return new EitherImpl<>(this, value, this.fallback); + } + + public EitherImpl withFallback(@Nullable V value) { + @Nullable V newValue = value; + if (this.fallback == newValue) return this; + return new EitherImpl<>(this, this.primary, newValue); + } + + public EitherImpl withFallback(Optional optional) { + @Nullable V value = optional.orElse(null); + if (this.fallback == value) return this; + return new EitherImpl<>(this, this.primary, value); + } + + @Override + public boolean equals(@Nullable Object another) { + if (this == another) return true; + return another instanceof EitherImpl + && equalTo((EitherImpl) another); + } + + private boolean equalTo(EitherImpl another) { + return Objects.equals(primary, another.primary) + && Objects.equals(fallback, another.fallback); + } + + @Override + public int hashCode() { + int h = 5381; + h += (h << 5) + Objects.hashCode(primary); + h += (h << 5) + Objects.hashCode(fallback); + return h; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("Either{"); + if (primary != null) { + builder.append("primary=").append(primary); + } + if (fallback != null) { + if (builder.length() > 7) builder.append(", "); + builder.append("fallback=").append(fallback); + } + return builder.append("}").toString(); + } + + public static EitherImpl of(Optional primary, Optional fallback) { + return new EitherImpl<>(primary, fallback); + } + + public static EitherImpl of(@Nullable U primary, @Nullable V fallback) { + return new EitherImpl<>(primary, fallback); + } + + public static EitherImpl copyOf(Either instance) { + if (instance instanceof EitherImpl) { + return (EitherImpl) instance; + } + return EitherImpl.of(instance.primary(), instance.fallback()); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/FileUtils.java b/common/src/main/java/net/momirealms/customcrops/common/util/FileUtils.java new file mode 100644 index 000000000..96dd57063 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/FileUtils.java @@ -0,0 +1,112 @@ +/* + * 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.common.util; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Utility class for handling file and directory operations. + */ +public class FileUtils { + + private FileUtils() {} + + /** + * Creates a file if it does not already exist. + * + * @param path the path to the file + * @return the path to the file + * @throws IOException if an I/O error occurs + */ + public static Path createFileIfNotExists(Path path) throws IOException { + if (!Files.exists(path)) { + Files.createFile(path); + } + return path; + } + + /** + * Creates a directory if it does not already exist. + * + * @param path the path to the directory + * @return the path to the directory + * @throws IOException if an I/O error occurs + */ + public static Path createDirectoryIfNotExists(Path path) throws IOException { + if (Files.exists(path) && (Files.isDirectory(path) || Files.isSymbolicLink(path))) { + return path; + } + + try { + Files.createDirectory(path); + } catch (FileAlreadyExistsException e) { + // ignore + } + + return path; + } + + /** + * Creates directories if they do not already exist. + * + * @param path the path to the directories + * @return the path to the directories + * @throws IOException if an I/O error occurs + */ + public static Path createDirectoriesIfNotExists(Path path) throws IOException { + if (Files.exists(path) && (Files.isDirectory(path) || Files.isSymbolicLink(path))) { + return path; + } + + try { + Files.createDirectories(path); + } catch (FileAlreadyExistsException e) { + // ignore + } + + return path; + } + + /** + * Deletes a directory and all its contents. + * + * @param path the path to the directory + * @throws IOException if an I/O error occurs + */ + public static void deleteDirectory(Path path) throws IOException { + if (!Files.exists(path) || !Files.isDirectory(path)) { + return; + } + + try (DirectoryStream contents = Files.newDirectoryStream(path)) { + for (Path file : contents) { + if (Files.isDirectory(file)) { + deleteDirectory(file); + } else { + Files.delete(file); + } + } + } + + Files.delete(path); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/Key.java b/common/src/main/java/net/momirealms/customcrops/common/util/Key.java new file mode 100644 index 000000000..2835d74d8 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/Key.java @@ -0,0 +1,56 @@ +package net.momirealms.customcrops.common.util; + +public class Key { + + private final String namespace; + private final String value; + + public Key(String namespace, String value) { + this.namespace = namespace; + this.value = value; + } + + public static Key key(String namespace, String value) { + return new Key(namespace, value); + } + + public static Key key(String key) { + int index = key.indexOf(":"); + String namespace = index >= 1 ? key.substring(0, index) : "minecraft"; + String value = index >= 0 ? key.substring(index + 1) : key; + return key(namespace, value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Key key = (Key) o; + return namespace.equals(key.namespace) && value.equals(key.value); + } + + @Override + public int hashCode() { + return asString().hashCode(); + } + + public String asString() { + return namespace + ":" + value; + } + + public String namespace() { + return namespace; + } + + public String value() { + return value; + } + + @Override + public String toString() { + return "Key{" + + "namespace='" + namespace + '\'' + + ", value='" + value + '\'' + + '}'; + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/ListUtils.java b/common/src/main/java/net/momirealms/customcrops/common/util/ListUtils.java new file mode 100644 index 000000000..7c31884bf --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/ListUtils.java @@ -0,0 +1,49 @@ +/* + * 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.common.util; + +import java.util.List; + +/** + * Utility class for handling operations related to lists. + */ +public class ListUtils { + + private ListUtils() { + } + + /** + * Converts an object to a list of strings. + * If the object is a string, it returns a list containing the string. + * If the object is a list, it casts and returns the list as a list of strings. + * + * @param obj the object to convert + * @return the resulting list of strings + * @throws IllegalArgumentException if the object cannot be converted to a list of strings + */ + @SuppressWarnings("unchecked") + public static List toList(final Object obj) { + if (obj instanceof String s) { + return List.of(s); + } else if (obj instanceof List list) { + return (List) list; + } else { + return List.of(); + } + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/Pair.java b/common/src/main/java/net/momirealms/customcrops/common/util/Pair.java new file mode 100644 index 000000000..40530a1ef --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/Pair.java @@ -0,0 +1,90 @@ +/* + * 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.common.util; + +/** + * A generic class representing a pair of values. + * This class provides methods to create and access pairs of values. + * + * @param the type of the left value + * @param the type of the right value + */ +public class Pair { + private L left; + private R right; + + /** + * Constructs a new {@link Pair} with the specified left and right values. + * + * @param left the left value + * @param right the right value + */ + public Pair(L left, R right) { + this.left = left; + this.right = right; + } + + /** + * Returns the left value. + * + * @return the left value + */ + public L left() { + return left; + } + + /** + * Sets the left value. + * + * @param left the new left value + */ + public void left(L left) { + this.left = left; + } + + /** + * Returns the right value. + * + * @return the right value + */ + public R right() { + return right; + } + + /** + * Sets the right value. + * + * @param right the new right value + */ + public void right(R right) { + this.right = right; + } + + /** + * Creates a new {@link Pair} with the specified left and right values. + * + * @param left the left value + * @param right the right value + * @param the type of the left value + * @param the type of the right value + * @return a new {@link Pair} with the specified values + */ + public static Pair of(final L left, final R right) { + return new Pair<>(left, right); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/RandomUtils.java b/common/src/main/java/net/momirealms/customcrops/common/util/RandomUtils.java new file mode 100644 index 000000000..9b784474c --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/RandomUtils.java @@ -0,0 +1,140 @@ +/* + * 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.common.util; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Utility class for generating random values. + */ +public class RandomUtils { + + private final Random random; + + private RandomUtils() { + random = ThreadLocalRandom.current(); + } + + /** + * Static inner class to hold the singleton instance of RandomUtils. + */ + private static class SingletonHolder { + private static final RandomUtils INSTANCE = new RandomUtils(); + } + + /** + * Retrieves the singleton instance of RandomUtils. + * + * @return the singleton instance + */ + private static RandomUtils getInstance() { + return SingletonHolder.INSTANCE; + } + + /** + * Generates a random integer between the specified minimum and maximum values (inclusive). + * + * @param min the minimum value + * @param max the maximum value + * @return a random integer between min and max (inclusive) + */ + public static int generateRandomInt(int min, int max) { + return getInstance().random.nextInt(max - min + 1) + min; + } + + /** + * Generates a random double between the specified minimum and maximum values (inclusive). + * + * @param min the minimum value + * @param max the maximum value + * @return a random double between min and max (inclusive) + */ + public static double generateRandomDouble(double min, double max) { + return min + (max - min) * getInstance().random.nextDouble(); + } + + /** + * Generates a random float between the specified minimum and maximum values (inclusive). + * + * @param min the minimum value + * @param max the maximum value + * @return a random float between min and max (inclusive) + */ + public static float generateRandomFloat(float min, float max) { + return min + (max - min) * getInstance().random.nextFloat(); + } + + /** + * Generates a random boolean value. + * + * @return a random boolean value + */ + public static boolean generateRandomBoolean() { + return getInstance().random.nextBoolean(); + } + + /** + * Selects a random element from the specified array. + * + * @param array the array to select a random element from + * @param the type of the elements in the array + * @return a random element from the array + */ + public static T getRandomElementFromArray(T[] array) { + int index = getInstance().random.nextInt(array.length); + return array[index]; + } + + /** + * Generates a random value based on a triangular distribution. + * + * @param mode the mode (peak) of the distribution + * @param deviation the deviation from the mode + * @return a random value based on a triangular distribution + */ + public static double triangle(double mode, double deviation) { + return mode + deviation * (generateRandomDouble(0,1) - generateRandomDouble(0,1)); + } + + /** + * Selects a specified number of random elements from the given array. + * + * @param array the array to select random elements from + * @param count the number of random elements to select + * @param the type of the elements in the array + * @return an array containing the selected random elements + * @throws IllegalArgumentException if the count is greater than the array length + */ + public static T[] getRandomElementsFromArray(T[] array, int count) { + if (count > array.length) { + throw new IllegalArgumentException("Count cannot be greater than array length"); + } + + @SuppressWarnings("unchecked") + T[] result = (T[]) new Object[count]; + + for (int i = 0; i < count; i++) { + int index = getInstance().random.nextInt(array.length - i); + result[i] = array[index]; + array[index] = array[array.length - i - 1]; + } + + return result; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionResult.java b/common/src/main/java/net/momirealms/customcrops/common/util/TriConsumer.java similarity index 78% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionResult.java rename to common/src/main/java/net/momirealms/customcrops/common/util/TriConsumer.java index a2036f55c..3a722d96c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionResult.java +++ b/common/src/main/java/net/momirealms/customcrops/common/util/TriConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,11 +15,8 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.item.function; +package net.momirealms.customcrops.common.util; -public enum FunctionResult { - - PASS, - RETURN, - CANCEL_EVENT_AND_RETURN -} +public interface TriConsumer { + void accept(K k, V v, S s); +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/Pair.java b/common/src/main/java/net/momirealms/customcrops/common/util/TriFunction.java similarity index 59% rename from api/src/main/java/net/momirealms/customcrops/api/common/Pair.java rename to common/src/main/java/net/momirealms/customcrops/common/util/TriFunction.java index 1c9ac5049..e30b9640a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/common/Pair.java +++ b/common/src/main/java/net/momirealms/customcrops/common/util/TriFunction.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,11 +15,18 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.common; +package net.momirealms.customcrops.common.util; -public record Pair(L left, R right) { +import java.util.Objects; +import java.util.function.Function; - public static Pair of(final L left, final R right) { - return new Pair<>(left, right); +public interface TriFunction { + R apply(T var1, U var2, V var3); + + default TriFunction andThen(Function after) { + Objects.requireNonNull(after); + return (t, u, v) -> { + return after.apply(this.apply(t, u, v)); + }; } } \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/Tristate.java b/common/src/main/java/net/momirealms/customcrops/common/util/Tristate.java new file mode 100644 index 000000000..5f3088822 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/Tristate.java @@ -0,0 +1,98 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.common.util; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents three different states of a setting. + * + *

Possible values:

+ *

+ *
    + *
  • {@link #TRUE} - a positive setting
  • + *
  • {@link #FALSE} - a negative (negated) setting
  • + *
  • {@link #UNDEFINED} - a non-existent setting
  • + *
+ */ +public enum Tristate { + + /** + * A value indicating a positive setting + */ + TRUE(true), + + /** + * A value indicating a negative (negated) setting + */ + FALSE(false), + + /** + * A value indicating a non-existent setting + */ + UNDEFINED(false); + + /** + * Returns a {@link Tristate} from a boolean + * + * @param val the boolean value + * @return {@link #TRUE} or {@link #FALSE}, if the value is true or false, respectively. + */ + public static @NotNull Tristate of(boolean val) { + return val ? TRUE : FALSE; + } + + /** + * Returns a {@link Tristate} from a nullable boolean. + * + *

Unlike {@link #of(boolean)}, this method returns {@link #UNDEFINED} + * if the value is null.

+ * + * @param val the boolean value + * @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value + * is null, true or false, respectively. + */ + public static @NotNull Tristate of(Boolean val) { + return val == null ? UNDEFINED : val ? TRUE : FALSE; + } + + private final boolean booleanValue; + + Tristate(boolean booleanValue) { + this.booleanValue = booleanValue; + } + + /** + * Returns the value of the Tristate as a boolean. + * + *

A value of {@link #UNDEFINED} converts to false.

+ * + * @return a boolean representation of the Tristate. + */ + public boolean asBoolean() { + return this.booleanValue; + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/Tuple.java b/common/src/main/java/net/momirealms/customcrops/common/util/Tuple.java new file mode 100644 index 000000000..0e27b09c0 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/Tuple.java @@ -0,0 +1,114 @@ +/* + * 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.common.util; + +/** + * A generic class representing a tuple with three values. + * This class provides methods for creating and accessing tuples with three values. + * + * @param the type of the left value + * @param the type of the middle value + * @param the type of the right value + */ +public class Tuple { + private L left; + private M mid; + private R right; + + /** + * Constructs a new {@link Tuple} with the specified left, middle, and right values. + * + * @param left the left value + * @param mid the middle value + * @param right the right value + */ + public Tuple(L left, M mid, R right) { + this.left = left; + this.mid = mid; + this.right = right; + } + + /** + * Returns the left value. + * + * @return the left value + */ + public L left() { + return left; + } + + /** + * Sets the left value. + * + * @param left the new left value + */ + public void left(L left) { + this.left = left; + } + + /** + * Returns the middle value. + * + * @return the middle value + */ + public M mid() { + return mid; + } + + /** + * Sets the middle value. + * + * @param mid the new middle value + */ + public void mid(M mid) { + this.mid = mid; + } + + /** + * Returns the right value. + * + * @return the right value + */ + public R right() { + return right; + } + + /** + * Sets the right value. + * + * @param right the new right value + */ + public void right(R right) { + this.right = right; + } + + /** + * Creates a new {@link Tuple} with the specified left, middle, and right values. + * + * @param left the left value + * @param mid the middle value + * @param right the right value + * @param the type of the left value + * @param the type of the middle value + * @param the type of the right value + * @return a new {@link Tuple} with the specified values + */ + public static Tuple of(final L left, final M mid, final R right) { + return new Tuple<>(left, mid, right); + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/UUIDUtils.java b/common/src/main/java/net/momirealms/customcrops/common/util/UUIDUtils.java new file mode 100644 index 000000000..43a23401d --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/UUIDUtils.java @@ -0,0 +1,87 @@ +/* + * 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.common.util; + +import java.math.BigInteger; +import java.util.UUID; + +/** + * Utility class for handling operations related to UUIDs. + * Provides methods for converting between different UUID formats and representations. + */ +public class UUIDUtils { + + /** + * Converts a UUID string without dashes to a {@link UUID} object. + * + * @param id the UUID string without dashes + * @return the corresponding {@link UUID} object, or null if the input string is null + */ + public static UUID fromUnDashedUUID(String id) { + return id == null ? null : new UUID( + new BigInteger(id.substring(0, 16), 16).longValue(), + new BigInteger(id.substring(16, 32), 16).longValue() + ); + } + + /** + * Converts a {@link UUID} object to a string without dashes. + * + * @param uuid the {@link UUID} object + * @return the UUID string without dashes + */ + public static String toUnDashedUUID(UUID uuid) { + return uuid.toString().replace("-", ""); + } + + /** + * Converts an integer array to a {@link UUID} object. + * The array must contain exactly four integers. + * + * @param array the integer array + * @return the corresponding {@link UUID} object + * @throws IllegalArgumentException if the array length is not four + */ + public static UUID uuidFromIntArray(int[] array) { + return new UUID((long)array[0] << 32 | (long)array[1] & 4294967295L, (long)array[2] << 32 | (long)array[3] & 4294967295L); + } + + /** + * Converts a {@link UUID} object to an integer array. + * The resulting array contains exactly four integers. + * + * @param uuid the {@link UUID} object + * @return the integer array representation of the UUID + */ + public static int[] uuidToIntArray(UUID uuid) { + long l = uuid.getMostSignificantBits(); + long m = uuid.getLeastSignificantBits(); + return leastMostToIntArray(l, m); + } + + /** + * Converts the most significant and least significant bits of a UUID to an integer array. + * + * @param uuidMost the most significant bits of the UUID + * @param uuidLeast the least significant bits of the UUID + * @return the integer array representation of the UUID bits + */ + private static int[] leastMostToIntArray(long uuidMost, long uuidLeast) { + return new int[]{(int)(uuidMost >> 32), (int)uuidMost, (int)(uuidLeast >> 32), (int)uuidLeast}; + } +} diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/WeightUtils.java b/common/src/main/java/net/momirealms/customcrops/common/util/WeightUtils.java new file mode 100644 index 000000000..e66eab005 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/WeightUtils.java @@ -0,0 +1,108 @@ +/* + * 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.common.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Utility class for selecting random items based on weights. + */ +@SuppressWarnings("DuplicatedCode") +public class WeightUtils { + + private WeightUtils() {} + + /** + * Get a random item from a list of pairs, each associated with a weight. + * + * @param pairs A list of pairs where the left element is the item and the right element is its weight. + * @param The type of items in the list. + * @return A randomly selected item from the list, or null if no item was selected. + */ + public static T getRandom(List> pairs) { + List available = new ArrayList<>(); + double[] weights = new double[pairs.size()]; + int index = 0; + for (Pair pair : pairs){ + double weight = pair.right(); + T key = pair.left(); + if (weight <= 0) continue; + available.add(key); + weights[index++] = weight; + } + return getRandom(weights, available, index); + } + + /** + * Get a random item from a map where each entry is associated with a weight. + * + * @param map A map where each entry's key is an item, and the value is its weight. + * @param The type of items in the map. + * @return A randomly selected item from the map, or null if no item was selected. + */ + public static T getRandom(Map map) { + List available = new ArrayList<>(); + double[] weights = new double[map.size()]; + int index = 0; + for (Map.Entry entry : map.entrySet()){ + double weight = entry.getValue(); + T key = entry.getKey(); + if (weight <= 0) continue; + available.add(key); + weights[index++] = weight; + } + return getRandom(weights, available, index); + } + + /** + * Get a random item from a list of items with associated weights. + * + * @param weights An array of weights corresponding to the available items. + * @param available A list of available items. + * @param effectiveSize The effective size of the array and list after filtering out items with non-positive weights. + * @param The type of items. + * @return A randomly selected item from the list, or null if no item was selected. + */ + private static T getRandom(double[] weights, List available, int effectiveSize) { + if (available.isEmpty()) return null; + double total = Arrays.stream(weights).sum(); + double[] weightRatios = new double[effectiveSize]; + for (int i = 0; i < effectiveSize; i++){ + weightRatios[i] = weights[i]/total; + } + double[] weightRange = new double[effectiveSize]; + double startPos = 0; + for (int i = 0; i < effectiveSize; i++) { + weightRange[i] = startPos + weightRatios[i]; + startPos += weightRatios[i]; + } + double random = Math.random(); + int pos = Arrays.binarySearch(weightRange, random); + + if (pos < 0) { + pos = -pos - 1; + } + if (pos < weightRange.length && random < weightRange[pos]) { + return available.get(pos); + } + return null; + } +} diff --git a/common/src/main/resources/custom-crops.properties b/common/src/main/resources/custom-crops.properties new file mode 100644 index 000000000..931312c22 --- /dev/null +++ b/common/src/main/resources/custom-crops.properties @@ -0,0 +1,20 @@ +builder=${builder} +git=${git_version} +config=${config_version} +asm=${asm_version} +asm-commons=${asm_commons_version} +jar-relocator=${jar_relocator_version} +cloud-core=${cloud_core_version} +cloud-brigadier=${cloud_brigadier_version} +cloud-services=${cloud_services_version} +cloud-bukkit=${cloud_bukkit_version} +cloud-paper=${cloud_paper_version} +cloud-minecraft-extras=${cloud_minecraft_extras_version} +boosted-yaml=${boosted_yaml_version} +bstats-base=${bstats_version} +geantyref=${geantyref_version} +gson=${gson_version} +caffeine=${caffeine_version} +exp4j=${exp4j_version} +slf4j=${slf4j_version} +zstd-jni=${zstd_version} \ No newline at end of file diff --git a/compatibility-asp-r1/build.gradle.kts b/compatibility-asp-r1/build.gradle.kts new file mode 100644 index 000000000..afff1d141 --- /dev/null +++ b/compatibility-asp-r1/build.gradle.kts @@ -0,0 +1,27 @@ +repositories { + mavenCentral() + maven("https://repo.rapture.pw/repository/maven-releases/") + maven("https://repo.infernalsuite.com/repository/maven-snapshots/") + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + compileOnly(project(":api")) + compileOnly(project(":common")) + compileOnly("dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT") + compileOnly("com.infernalsuite.aswm:api:1.20.4-R0.1-SNAPSHOT") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java new file mode 100644 index 000000000..b29042f67 --- /dev/null +++ b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java @@ -0,0 +1,227 @@ +package net.momirealms.customcrops.bukkit.integration.adaptor.asp_r1; + +import com.flowpowered.nbt.*; +import com.infernalsuite.aswm.api.world.SlimeWorld; +import net.momirealms.customcrops.common.util.Key; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.Registries; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.world.*; +import net.momirealms.customcrops.api.core.world.adaptor.AbstractWorldAdaptor; +import net.momirealms.customcrops.api.util.StringUtils; +import net.momirealms.customcrops.common.helper.GsonHelper; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class SlimeWorldAdaptorR1 extends AbstractWorldAdaptor { + + private final Function getSlimeWorldFunction; + + public SlimeWorldAdaptorR1(int version) { + try { + if (version == 1) { + Plugin plugin = Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); + Class slimeClass = Class.forName("com.infernalsuite.aswm.api.SlimePlugin"); + Method method = slimeClass.getMethod("getWorld", String.class); + this.getSlimeWorldFunction = (name) -> { + try { + return (SlimeWorld) method.invoke(plugin, name); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }; + } else if (version == 2) { + Class apiClass = Class.forName("com.infernalsuite.aswm.api.AdvancedSlimePaperAPI"); + Object apiInstance = apiClass.getMethod("instance").invoke(null); + Method method = apiClass.getMethod("getLoadedWorld", String.class); + this.getSlimeWorldFunction = (name) -> { + try { + return (SlimeWorld) method.invoke(apiInstance, name); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }; + } else { + throw new IllegalArgumentException("Unsupported version: " + version); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public CustomCropsWorld adapt(Object world) { + return CustomCropsWorld.create((SlimeWorld) world, this); + } + + @Override + public SlimeWorld getWorld(String worldName) { + return getSlimeWorldFunction.apply(worldName); + } + + private CompoundMap createOrGetDataMap(SlimeWorld world) { + Optional optionalCompoundTag = world.getExtraData().getAsCompoundTag("customcrops"); + CompoundMap ccDataMap; + if (optionalCompoundTag.isEmpty()) { + ccDataMap = new CompoundMap(); + world.getExtraData().getValue().put(new CompoundTag("customcrops", ccDataMap)); + } else { + ccDataMap = optionalCompoundTag.get().getValue(); + } + return ccDataMap; + } + + @Override + public WorldExtraData loadExtraData(SlimeWorld world) { + CompoundMap ccDataMap = createOrGetDataMap(world); + String json = Optional.ofNullable(ccDataMap.get("world-info")).map(tag -> tag.getAsStringTag().get().getValue()).orElse(null); + return (json == null || json.equals("null")) ? WorldExtraData.empty() : GsonHelper.get().fromJson(json, WorldExtraData.class); + } + + @Override + public void saveExtraData(CustomCropsWorld world) { + CompoundMap ccDataMap = createOrGetDataMap(world.world()); + ccDataMap.put(new StringTag("world-info", GsonHelper.get().toJson(world.extraData()))); + } + + @Nullable + @Override + public CustomCropsRegion loadRegion(CustomCropsWorld world, RegionPos pos, boolean createIfNotExists) { + return null; + } + + @Nullable + @Override + public CustomCropsChunk loadChunk(CustomCropsWorld world, ChunkPos pos, boolean createIfNotExists) { + long time1 = System.currentTimeMillis(); + CompoundMap ccDataMap = createOrGetDataMap(world.world()); + Tag chunkTag = ccDataMap.get(pos.asString()); + if (chunkTag == null) { + return createIfNotExists ? world.createChunk(pos) : null; + } + Optional chunkCompoundTag = chunkTag.getAsCompoundTag(); + if (chunkCompoundTag.isEmpty()) { + return createIfNotExists ? world.createChunk(pos) : null; + } + CustomCropsChunk chunk = tagToChunk(world, chunkCompoundTag.get()); + long time2 = System.currentTimeMillis(); + BukkitCustomCropsPlugin.getInstance().debug("Took " + (time2-time1) + "ms to load chunk " + pos); + return chunk; + } + + @Override + public void saveRegion(CustomCropsWorld world, CustomCropsRegion region) {} + + @Override + public void saveChunk(CustomCropsWorld world, CustomCropsChunk chunk) { + CompoundMap ccDataMap = createOrGetDataMap(world.world()); + SerializableChunk serializableChunk = toSerializableChunk(chunk); + Runnable runnable = () -> { + if (serializableChunk.canPrune()) { + ccDataMap.remove(chunk.chunkPos().asString()); + } else { + ccDataMap.put(chunkToTag(serializableChunk)); + } + }; + if (Bukkit.isPrimaryThread()) { + runnable.run(); + } else { + BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run(runnable, null); + } + } + + @Override + public String getName(SlimeWorld world) { + return world.getName(); + } + + @Override + public long getWorldFullTime(SlimeWorld world) { + return Objects.requireNonNull(Bukkit.getWorld(world.getName())).getFullTime(); + } + + @Override + public int priority() { + return SLIME_WORLD_PRIORITY; + } + + private CompoundTag chunkToTag(SerializableChunk serializableChunk) { + CompoundMap map = new CompoundMap(); + map.put(new IntTag("x", serializableChunk.x())); + map.put(new IntTag("z", serializableChunk.z())); + map.put(new IntTag("version", CHUNK_VERSION)); + map.put(new IntTag("loadedSeconds", serializableChunk.loadedSeconds())); + map.put(new LongTag("lastLoadedTime", serializableChunk.lastLoadedTime())); + map.put(new IntArrayTag("queued", serializableChunk.queuedTasks())); + map.put(new IntArrayTag("ticked", serializableChunk.ticked())); + CompoundMap sectionMap = new CompoundMap(); + for (SerializableSection section : serializableChunk.sections()) { + sectionMap.put(new ListTag<>(String.valueOf(section.sectionID()), TagType.TAG_COMPOUND, section.blocks())); + } + map.put(new CompoundTag("sections", sectionMap)); + return new CompoundTag(serializableChunk.x() + "," + serializableChunk.z(), map); + } + + @SuppressWarnings("all") + private CustomCropsChunk tagToChunk(CustomCropsWorld world, CompoundTag tag) { + CompoundMap map = tag.getValue(); + int version = map.get("version").getAsIntTag().orElse(new IntTag("version", 1)).getValue(); + Function keyFunction = version < 2 ? + (s) -> { + return Key.key("customcrops", StringUtils.toLowerCase(s)); + } : s -> { + return Key.key(s); + }; + int x = (int) map.get("x").getValue(); + int z = (int) map.get("z").getValue(); + + ChunkPos coordinate = new ChunkPos(x, z); + int loadedSeconds = (int) map.get("loadedSeconds").getValue(); + long lastLoadedTime = (long) map.get("lastLoadedTime").getValue(); + int[] queued = (int[]) map.get("queued").getValue(); + int[] ticked = (int[]) map.get("ticked").getValue(); + + PriorityQueue queue = new PriorityQueue<>(Math.max(11, queued.length / 2)); + for (int i = 0, size = queued.length / 2; i < size; i++) { + BlockPos pos = new BlockPos(queued[2*i+1]); + queue.add(new DelayedTickTask(queued[2*i], pos)); + } + + HashSet tickedSet = new HashSet<>(Math.max(11, ticked.length)); + for (int tick : ticked) { + tickedSet.add(new BlockPos(tick)); + } + + ConcurrentHashMap sectionMap = new ConcurrentHashMap<>(); + CompoundMap sectionCompoundMap = (CompoundMap) map.get("sections").getValue(); + for (Map.Entry> entry : sectionCompoundMap.entrySet()) { + if (entry.getValue() instanceof ListTag listTag) { + int id = Integer.parseInt(entry.getKey()); + ConcurrentHashMap blockMap = new ConcurrentHashMap<>(); + ListTag blocks = (ListTag) listTag; + for (CompoundTag blockTag : blocks.getValue()) { + CompoundMap block = blockTag.getValue(); + CompoundMap data = (CompoundMap) block.get("data").getValue(); + Key key = keyFunction.apply((String) block.get("type").getValue()); + CustomCropsBlock customBlock = Registries.BLOCK.get(key); + if (customBlock == null) { + BukkitCustomCropsPlugin.getInstance().getInstance().getPluginLogger().warn("[" + world.worldName() + "] Unrecognized custom block " + key + " has been removed from chunk " + ChunkPos.of(x, z)); + continue; + } + for (int pos : (int[]) block.get("pos").getValue()) { + BlockPos blockPos = new BlockPos(pos); + blockMap.put(blockPos, CustomCropsBlockState.create(customBlock, data)); + } + } + sectionMap.put(id, CustomCropsSection.restore(id, blockMap)); + } + } + return world.restoreChunk(coordinate, loadedSeconds, lastLoadedTime, sectionMap, queue, tickedSet); + } +} diff --git a/oraxen-legacy/build.gradle.kts b/compatibility-itemsadder-r1/build.gradle.kts similarity index 59% rename from oraxen-legacy/build.gradle.kts rename to compatibility-itemsadder-r1/build.gradle.kts index 2506dc038..18aec11b8 100644 --- a/oraxen-legacy/build.gradle.kts +++ b/compatibility-itemsadder-r1/build.gradle.kts @@ -1,12 +1,14 @@ +repositories { + mavenCentral() + maven("https://jitpack.io/") + maven("https://repo.papermc.io/repository/maven-public/") +} + dependencies { compileOnly(project(":api")) + compileOnly(project(":common")) compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") - compileOnly("com.github.oraxen:oraxen:1.172.0") -} - -tasks.withType { - options.encoding = "UTF-8" - options.release.set(17) + compileOnly("com.github.LoneDev6:api-itemsadder:3.6.3-beta-14") } java { @@ -15,4 +17,10 @@ java { toolchain { languageVersion = JavaLanguageVersion.of(17) } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) } \ No newline at end of file diff --git a/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java new file mode 100644 index 000000000..217383d14 --- /dev/null +++ b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java @@ -0,0 +1,79 @@ +package net.momirealms.customcrops.bukkit.integration.custom.itemsadder_r1; + +import dev.lone.itemsadder.api.Events.*; +import net.momirealms.customcrops.api.core.AbstractCustomEventListener; +import net.momirealms.customcrops.api.core.AbstractItemManager; +import net.momirealms.customcrops.api.util.FakeCancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.inventory.EquipmentSlot; + +public class ItemsAdderListener extends AbstractCustomEventListener { + + public ItemsAdderListener(AbstractItemManager itemManager) { + super(itemManager); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractFurniture(FurnitureInteractEvent event) { + itemManager.handlePlayerInteractFurniture( + event.getPlayer(), + event.getBukkitEntity().getLocation(), event.getNamespacedID(), + EquipmentSlot.HAND, event.getPlayer().getInventory().getItemInMainHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractCustomBlock(CustomBlockInteractEvent event) { + itemManager.handlePlayerInteractBlock( + event.getPlayer(), + event.getBlockClicked(), + event.getNamespacedID(), event.getBlockFace(), + event.getHand(), + event.getItem(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakFurniture(FurnitureBreakEvent event) { + itemManager.handlePlayerBreak( + event.getPlayer(), + event.getBukkitEntity().getLocation(), event.getPlayer().getInventory().getItemInMainHand(), event.getNamespacedID(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakCustomBlock(CustomBlockBreakEvent event) { + itemManager.handlePlayerBreak( + event.getPlayer(), + event.getBlock().getLocation(), event.getPlayer().getInventory().getItemInMainHand(), event.getNamespacedID(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceCustomBlock(CustomBlockPlaceEvent event) { + itemManager.handlePlayerPlace( + event.getPlayer(), + event.getBlock().getLocation(), + event.getNamespacedID(), + EquipmentSlot.HAND, + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceFurniture(FurniturePlaceSuccessEvent event) { + itemManager.handlePlayerPlace( + event.getPlayer(), + event.getBukkitEntity().getLocation(), + event.getNamespacedID(), + EquipmentSlot.HAND, + event.getPlayer().getInventory().getItemInMainHand(), + new FakeCancellable() + ); + } +} diff --git a/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderProvider.java b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderProvider.java new file mode 100644 index 000000000..23f0f439c --- /dev/null +++ b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderProvider.java @@ -0,0 +1,104 @@ +package net.momirealms.customcrops.bukkit.integration.custom.itemsadder_r1; + +import dev.lone.itemsadder.api.CustomBlock; +import dev.lone.itemsadder.api.CustomFurniture; +import dev.lone.itemsadder.api.CustomStack; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.CustomItemProvider; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class ItemsAdderProvider implements CustomItemProvider { + + @Override + public boolean removeCustomBlock(Location location) { + return CustomBlock.remove(location); + } + + @Override + public boolean placeCustomBlock(Location location, String id) { + CustomBlock block = CustomBlock.place(id, location); + if (block == null) { + CustomStack furniture = CustomFurniture.getInstance(id); + if (furniture == null) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Detected that you mistakenly configured the custom block[" + id + "] as furniture."); + } else { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Detected that custom block[" + id + "] doesn't exist in ItemsAdder configs. Please double check if that block exists."); + } + return false; + } + return true; + } + + @Override + public Entity placeFurniture(Location location, String id) { + try { + CustomFurniture furniture = CustomFurniture.spawnPreciseNonSolid(id, location); + if (furniture == null) return null; + return furniture.getEntity(); + } catch (RuntimeException e) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Failed to place furniture[" + id + "]. If this is not a problem caused by furniture not existing, consider increasing max-furniture-vehicles-per-chunk in ItemsAdder config.yml.", e); + return null; + } + } + + @Override + public boolean removeFurniture(Entity entity) { + if (isFurniture(entity)) { + CustomFurniture.remove(entity, false); + return true; + } else { + return false; + } + } + + @Override + public String blockID(Block block) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + if (customBlock == null) { + return null; + } + return customBlock.getNamespacedID(); + } + + @Override + public String itemID(ItemStack itemStack) { + CustomStack customStack = CustomStack.byItemStack(itemStack); + if (customStack == null) { + return null; + } + return customStack.getNamespacedID(); + } + + @Override + public ItemStack itemStack(Player player, String id) { + if (id == null) return new ItemStack(Material.AIR); + CustomStack customStack = CustomStack.getInstance(id); + if (customStack == null) { + return null; + } + return customStack.getItemStack().clone(); + } + + @Override + public String furnitureID(Entity entity) { + CustomFurniture customFurniture = CustomFurniture.byAlreadySpawned(entity); + if (customFurniture == null) { + return null; + } + return customFurniture.getNamespacedID(); + } + + @Override + public boolean isFurniture(Entity entity) { + try { + return CustomFurniture.byAlreadySpawned(entity) != null; + } catch (Exception e) { + return false; + } + } +} diff --git a/legacy-api/.gitignore b/compatibility-oraxen-r1/.gitignore similarity index 100% rename from legacy-api/.gitignore rename to compatibility-oraxen-r1/.gitignore diff --git a/compatibility-oraxen-r1/build.gradle.kts b/compatibility-oraxen-r1/build.gradle.kts new file mode 100644 index 000000000..1c3cabce6 --- /dev/null +++ b/compatibility-oraxen-r1/build.gradle.kts @@ -0,0 +1,25 @@ +repositories { + mavenCentral() + maven("https://repo.oraxen.com/releases/") + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + compileOnly(project(":api")) + compileOnly(project(":common")) + compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") + compileOnly("io.th0rgal:oraxen:1.180.0") +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} \ No newline at end of file diff --git a/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenListener.java b/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenListener.java new file mode 100644 index 000000000..a9a9e306b --- /dev/null +++ b/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenListener.java @@ -0,0 +1,118 @@ +package net.momirealms.customcrops.bukkit.integration.custom.oraxen_r1; + +import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; +import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockBreakEvent; +import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockInteractEvent; +import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockPlaceEvent; +import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockBreakEvent; +import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockInteractEvent; +import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockPlaceEvent; +import net.momirealms.customcrops.api.core.AbstractCustomEventListener; +import net.momirealms.customcrops.api.core.AbstractItemManager; +import org.bukkit.event.EventHandler; + +public class OraxenListener extends AbstractCustomEventListener { + + public OraxenListener(AbstractItemManager itemManager) { + super(itemManager); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractFurniture(OraxenFurnitureInteractEvent event) { + itemManager.handlePlayerInteractFurniture( + event.getPlayer(), + event.getBaseEntity().getLocation(), event.getMechanic().getItemID(), + event.getHand(), event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractCustomBlock(OraxenNoteBlockInteractEvent event) { + itemManager.handlePlayerInteractBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), event.getBlockFace(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractCustomBlock(OraxenStringBlockInteractEvent event) { + itemManager.handlePlayerInteractBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), event.getBlockFace(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakFurniture(OraxenFurnitureBreakEvent event) { + itemManager.handlePlayerBreak( + event.getPlayer(), + event.getBaseEntity().getLocation(), event.getPlayer().getInventory().getItemInMainHand(), event.getMechanic().getItemID(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakCustomBlock(OraxenNoteBlockBreakEvent event) { + itemManager.handlePlayerBreak( + event.getPlayer(), + event.getBlock().getLocation(), event.getPlayer().getInventory().getItemInMainHand(), event.getMechanic().getItemID(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakCustomBlock(OraxenStringBlockBreakEvent event) { + itemManager.handlePlayerBreak( + event.getPlayer(), + event.getBlock().getLocation(), event.getPlayer().getInventory().getItemInMainHand(), event.getMechanic().getItemID(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceFurniture(OraxenFurniturePlaceEvent event) { + itemManager.handlePlayerPlace( + event.getPlayer(), + event.getBaseEntity().getLocation(), + event.getMechanic().getItemID(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceCustomBlock(OraxenNoteBlockPlaceEvent event) { + itemManager.handlePlayerPlace( + event.getPlayer(), + event.getBlock().getLocation(), + event.getMechanic().getItemID(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceCustomBlock(OraxenStringBlockPlaceEvent event) { + itemManager.handlePlayerPlace( + event.getPlayer(), + event.getBlock().getLocation(), + event.getMechanic().getItemID(), + event.getHand(), + event.getItemInHand(), + event + ); + } +} diff --git a/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenProvider.java b/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenProvider.java new file mode 100644 index 000000000..6be0cb5f5 --- /dev/null +++ b/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenProvider.java @@ -0,0 +1,97 @@ +package net.momirealms.customcrops.bukkit.integration.custom.oraxen_r1; + +import io.th0rgal.oraxen.api.OraxenBlocks; +import io.th0rgal.oraxen.api.OraxenFurniture; +import io.th0rgal.oraxen.api.OraxenItems; +import io.th0rgal.oraxen.items.ItemBuilder; +import io.th0rgal.oraxen.mechanics.Mechanic; +import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.CustomItemProvider; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Rotation; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class OraxenProvider implements CustomItemProvider { + + @Override + public boolean removeCustomBlock(Location location) { + Block block = location.getBlock(); + if (OraxenBlocks.isOraxenBlock(block)) { + block.setType(Material.AIR, false); + return true; + } + return false; + } + + @Override + public boolean placeCustomBlock(Location location, String id) { + if (OraxenItems.exists(id)) { + OraxenBlocks.place(id, location); + return true; + } else { + return false; + } + } + + @Override + public @Nullable Entity placeFurniture(Location location, String id) { + Entity entity = OraxenFurniture.place(id, location, Rotation.NONE, BlockFace.UP); + if (entity == null) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Furniture[" + id +"] doesn't exist. Please double check if that furniture exists."); + } + return entity; + } + + @Override + public boolean removeFurniture(Entity entity) { + if (isFurniture(entity)) { + OraxenFurniture.remove(entity, null, null); + return true; + } + return false; + } + + @Override + public @Nullable String blockID(Block block) { + Mechanic mechanic = OraxenBlocks.getOraxenBlock(block.getBlockData()); + if (mechanic == null) { + return null; + } + return mechanic.getItemID(); + } + + @Override + public @Nullable String itemID(ItemStack itemStack) { + return OraxenItems.getIdByItem(itemStack); + } + + @Override + public @Nullable ItemStack itemStack(Player player, String id) { + ItemBuilder builder = OraxenItems.getItemById(id); + if (builder == null) { + return null; + } + return builder.build(); + } + + @Override + public @Nullable String furnitureID(Entity entity) { + FurnitureMechanic mechanic = OraxenFurniture.getFurnitureMechanic(entity); + if (mechanic == null) { + return null; + } + return mechanic.getItemID(); + } + + @Override + public boolean isFurniture(Entity entity) { + return OraxenFurniture.isFurniture(entity); + } +} diff --git a/oraxen-j21/.gitignore b/compatibility-oraxen-r2/.gitignore similarity index 100% rename from oraxen-j21/.gitignore rename to compatibility-oraxen-r2/.gitignore diff --git a/oraxen-j21/build.gradle.kts b/compatibility-oraxen-r2/build.gradle.kts similarity index 70% rename from oraxen-j21/build.gradle.kts rename to compatibility-oraxen-r2/build.gradle.kts index 9c4c0912c..170df2ecb 100644 --- a/oraxen-j21/build.gradle.kts +++ b/compatibility-oraxen-r2/build.gradle.kts @@ -1,5 +1,12 @@ +repositories { + mavenCentral() + maven("https://repo.oraxen.com/snapshots/") + maven("https://repo.papermc.io/repository/maven-public/") +} + dependencies { compileOnly(project(":api")) + compileOnly(project(":common")) compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") compileOnly("io.th0rgal:oraxen:2.0-SNAPSHOT") } diff --git a/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenListener.java b/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenListener.java new file mode 100644 index 000000000..c6a0b2c7b --- /dev/null +++ b/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenListener.java @@ -0,0 +1,118 @@ +package net.momirealms.customcrops.bukkit.integration.custom.oraxen_r2; + +import io.th0rgal.oraxen.api.events.custom_block.noteblock.OraxenNoteBlockBreakEvent; +import io.th0rgal.oraxen.api.events.custom_block.noteblock.OraxenNoteBlockInteractEvent; +import io.th0rgal.oraxen.api.events.custom_block.noteblock.OraxenNoteBlockPlaceEvent; +import io.th0rgal.oraxen.api.events.custom_block.stringblock.OraxenStringBlockBreakEvent; +import io.th0rgal.oraxen.api.events.custom_block.stringblock.OraxenStringBlockInteractEvent; +import io.th0rgal.oraxen.api.events.custom_block.stringblock.OraxenStringBlockPlaceEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; +import net.momirealms.customcrops.api.core.AbstractCustomEventListener; +import net.momirealms.customcrops.api.core.AbstractItemManager; +import org.bukkit.event.EventHandler; + +public class OraxenListener extends AbstractCustomEventListener { + + public OraxenListener(AbstractItemManager itemManager) { + super(itemManager); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractFurniture(OraxenFurnitureInteractEvent event) { + itemManager.handlePlayerInteractFurniture( + event.player(), + event.baseEntity().getLocation(), event.mechanic().getItemID(), + event.hand(), event.itemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractCustomBlock(OraxenNoteBlockInteractEvent event) { + itemManager.handlePlayerInteractBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), event.getBlockFace(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInteractCustomBlock(OraxenStringBlockInteractEvent event) { + itemManager.handlePlayerInteractBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), event.getBlockFace(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakFurniture(OraxenFurnitureBreakEvent event) { + itemManager.handlePlayerBreak( + event.getPlayer(), + event.getBaseEntity().getLocation(), event.getPlayer().getInventory().getItemInMainHand(), event.getMechanic().getItemID(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakCustomBlock(OraxenNoteBlockBreakEvent event) { + itemManager.handlePlayerBreak( + event.getPlayer(), + event.getBlock().getLocation(), event.getPlayer().getInventory().getItemInMainHand(), event.getMechanic().getItemID(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onBreakCustomBlock(OraxenStringBlockBreakEvent event) { + itemManager.handlePlayerBreak( + event.getPlayer(), + event.getBlock().getLocation(), event.getPlayer().getInventory().getItemInMainHand(), event.getMechanic().getItemID(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceFurniture(OraxenFurniturePlaceEvent event) { + itemManager.handlePlayerPlace( + event.getPlayer(), + event.getBaseEntity().getLocation(), + event.getMechanic().getItemID(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceCustomBlock(OraxenNoteBlockPlaceEvent event) { + itemManager.handlePlayerPlace( + event.getPlayer(), + event.getBlock().getLocation(), + event.getMechanic().getItemID(), + event.getHand(), + event.getItemInHand(), + event + ); + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceCustomBlock(OraxenStringBlockPlaceEvent event) { + itemManager.handlePlayerPlace( + event.getPlayer(), + event.getBlock().getLocation(), + event.getMechanic().getItemID(), + event.getHand(), + event.getItemInHand(), + event + ); + } +} diff --git a/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenProvider.java b/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenProvider.java new file mode 100644 index 000000000..7999c173c --- /dev/null +++ b/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenProvider.java @@ -0,0 +1,97 @@ +package net.momirealms.customcrops.bukkit.integration.custom.oraxen_r2; + +import io.th0rgal.oraxen.api.OraxenBlocks; +import io.th0rgal.oraxen.api.OraxenFurniture; +import io.th0rgal.oraxen.api.OraxenItems; +import io.th0rgal.oraxen.items.ItemBuilder; +import io.th0rgal.oraxen.mechanics.Mechanic; +import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.CustomItemProvider; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Rotation; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class OraxenProvider implements CustomItemProvider { + + @Override + public boolean removeCustomBlock(Location location) { + Block block = location.getBlock(); + if (OraxenBlocks.isCustomBlock(block)) { + block.setType(Material.AIR, false); + return true; + } + return false; + } + + @Override + public boolean placeCustomBlock(Location location, String id) { + if (OraxenItems.exists(id)) { + OraxenBlocks.place(id, location); + return true; + } else { + return false; + } + } + + @Override + public @Nullable Entity placeFurniture(Location location, String id) { + Entity entity = OraxenFurniture.place(id, location, Rotation.NONE, BlockFace.UP); + if (entity == null) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Furniture[" + id +"] doesn't exist. Please double check if that furniture exists."); + } + return entity; + } + + @Override + public boolean removeFurniture(Entity entity) { + if (isFurniture(entity)) { + OraxenFurniture.remove(entity, null, null); + return true; + } + return false; + } + + @Override + public @Nullable String blockID(Block block) { + Mechanic mechanic = OraxenBlocks.getCustomBlockMechanic(block.getBlockData()); + if (mechanic == null) { + return null; + } + return mechanic.getItemID(); + } + + @Override + public @Nullable String itemID(ItemStack itemStack) { + return OraxenItems.getIdByItem(itemStack); + } + + @Override + public @Nullable ItemStack itemStack(Player player, String id) { + ItemBuilder builder = OraxenItems.getItemById(id); + if (builder == null) { + return null; + } + return builder.build(); + } + + @Override + public @Nullable String furnitureID(Entity entity) { + FurnitureMechanic mechanic = OraxenFurniture.getFurnitureMechanic(entity); + if (mechanic == null) { + return null; + } + return mechanic.getItemID(); + } + + @Override + public boolean isFurniture(Entity entity) { + return OraxenFurniture.isFurniture(entity); + } +} diff --git a/compatibility/build.gradle.kts b/compatibility/build.gradle.kts new file mode 100644 index 000000000..f2bea9bb1 --- /dev/null +++ b/compatibility/build.gradle.kts @@ -0,0 +1,72 @@ +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://maven.enginehub.org/repo/") // worldguard worldedit + maven("https://jitpack.io/") + maven("https://papermc.io/repo/repository/maven-public/") // paper + maven("https://mvn.lumine.io/repository/maven-public/") // mythicmobs + maven("https://nexus.phoenixdevt.fr/repository/maven-public/") // mmoitems + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi + maven("https://r.irepo.space/maven/") // neigeitems + maven("https://repo.oraxen.com/releases/") // oraxen + maven("https://repo.auxilor.io/repository/maven-public/") // eco + maven("https://nexus.betonquest.org/repository/betonquest/") // betonquest + maven("https://repo.dmulloy2.net/repository/public/") // betonquest needs packet wrapper? + maven("https://oss.sonatype.org/content/repositories/snapshots") +} + +dependencies { + compileOnly(project(":common")) + compileOnly(project(":api")) + compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + compileOnly("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") { + exclude(module = "adventure-bom") + exclude(module = "checker-qual") + exclude(module = "annotations") + } + // server + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + // papi + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") + // vault + compileOnly("com.github.MilkBowl:VaultAPI:${rootProject.properties["vault_version"]}") + // season + compileOnly(files("libs/RealisticSeasons-api.jar")) + compileOnly(files("libs/AdvancedSeasons-API.jar")) + // leveler + compileOnly(files("libs/mcMMO-api.jar")) + compileOnly("net.Indyuce:MMOCore-API:1.12.1-SNAPSHOT") + compileOnly("dev.aurelium:auraskills-api-bukkit:2.1.5") + compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21") + compileOnly("com.github.Zrips:Jobs:v5.2.2.3") + // quest + compileOnly(files("libs/BattlePass-4.0.6-api.jar")) + compileOnly(files("libs/ClueScrolls-4.8.7-api.jar")) + compileOnly("org.betonquest:betonquest:2.1.3") + // item + compileOnly(files("libs/zaphkiel-2.0.24.jar")) + compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT") + compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT") + compileOnly("pers.neige.neigeitems:NeigeItems:1.17.13") + compileOnly("io.lumine:Mythic-Dist:5.6.2") + compileOnly("com.github.Xiao-MoMi:Custom-Fishing:2.2.19") + // eco + compileOnly("com.willfp:eco:6.70.1") + compileOnly("com.willfp:EcoJobs:3.56.1") + compileOnly("com.willfp:EcoSkills:3.46.1") + compileOnly("com.willfp:libreforge:4.58.1") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/plugin/libs/AdvancedSeasons-API.jar b/compatibility/libs/AdvancedSeasons-API.jar similarity index 100% rename from plugin/libs/AdvancedSeasons-API.jar rename to compatibility/libs/AdvancedSeasons-API.jar diff --git a/plugin/libs/BattlePass-4.0.6-api.jar b/compatibility/libs/BattlePass-4.0.6-api.jar similarity index 100% rename from plugin/libs/BattlePass-4.0.6-api.jar rename to compatibility/libs/BattlePass-4.0.6-api.jar diff --git a/plugin/libs/ClueScrolls-api.jar b/compatibility/libs/ClueScrolls-api.jar similarity index 100% rename from plugin/libs/ClueScrolls-api.jar rename to compatibility/libs/ClueScrolls-api.jar diff --git a/plugin/libs/RealisticSeasons-api.jar b/compatibility/libs/RealisticSeasons-api.jar similarity index 100% rename from plugin/libs/RealisticSeasons-api.jar rename to compatibility/libs/RealisticSeasons-api.jar diff --git a/plugin/libs/mcMMO-api.jar b/compatibility/libs/mcMMO-api.jar similarity index 100% rename from plugin/libs/mcMMO-api.jar rename to compatibility/libs/mcMMO-api.jar diff --git a/plugin/libs/zaphkiel-2.0.24.jar b/compatibility/libs/zaphkiel-2.0.24.jar similarity index 100% rename from plugin/libs/zaphkiel-2.0.24.jar rename to compatibility/libs/zaphkiel-2.0.24.jar diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/VaultHook.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/VaultHook.java new file mode 100644 index 000000000..4937305a0 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/VaultHook.java @@ -0,0 +1,79 @@ +/* + * 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.bukkit.integration; + +import net.milkbowl.vault.economy.Economy; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; + +public class VaultHook { + + private static boolean isHooked = false; + + public static void init() { + Singleton.initialize(); + VaultHook.isHooked = true; + } + + public static boolean isHooked() { + return isHooked; + } + + public static void deposit(Player player, double amount) { + Singleton.deposit(player, amount); + } + + public static void withdraw(OfflinePlayer player, double amount) { + Singleton.withdraw(player, amount); + } + + public static double getBalance(OfflinePlayer player) { + return Singleton.getBalance(player); + } + + private static class Singleton { + private static Economy economy; + + private static boolean initialize() { + RegisteredServiceProvider rsp = Bukkit.getServicesManager().getRegistration(Economy.class); + if (rsp == null) { + return false; + } + economy = rsp.getProvider(); + return true; + } + + private static Economy getEconomy() { + return economy; + } + + private static void deposit(OfflinePlayer player, double amount) { + economy.depositPlayer(player, amount); + } + + private static void withdraw(OfflinePlayer player, double amount) { + economy.withdrawPlayer(player, amount); + } + + private static double getBalance(OfflinePlayer player) { + return economy.getBalance(player); + } + } +} diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/CustomFishingItemProvider.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/CustomFishingItemProvider.java new file mode 100644 index 000000000..c7260a966 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/CustomFishingItemProvider.java @@ -0,0 +1,57 @@ +/* + * 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.bukkit.integration.item; + +import net.momirealms.customcrops.api.integration.ItemProvider; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +public class CustomFishingItemProvider implements ItemProvider { + + @Override + public String identifier() { + return "CustomFishing"; + } + + @NotNull + @Override + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { + String[] split = id.split(":", 2); + String finalID; + if (split.length == 1) { + // CustomFishing:ID + finalID = split[0]; + } else { + // CustomFishing:TYPE:ID + finalID = split[1]; + } + ItemStack itemStack = BukkitCustomFishingPlugin.getInstance().getItemManager().buildInternal(Context.player(player).arg(ContextKeys.ID, finalID), finalID); + return requireNonNull(itemStack); + } + + @Override + public String itemID(@NotNull ItemStack itemStack) { + return BukkitCustomFishingPlugin.getInstance().getItemManager().getCustomFishingItemID(itemStack); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MMOItemsItemProvider.java similarity index 66% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MMOItemsItemProvider.java index 8336b1690..2c8fb231e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MMOItemsItemProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,37 +15,41 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.item; +package net.momirealms.customcrops.bukkit.integration.item; import io.lumine.mythic.lib.api.item.NBTItem; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem; -import net.momirealms.customcrops.api.integration.ItemLibrary; +import net.momirealms.customcrops.api.integration.ItemProvider; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import java.util.Locale; -public class MMOItemsItemImpl implements ItemLibrary { +import static java.util.Objects.requireNonNull; + +public class MMOItemsItemProvider implements ItemProvider { @Override - public String identification() { + public String identifier() { return "MMOItems"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { String[] split = id.split(":"); MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ENGLISH)); - return mmoItem == null ? new ItemStack(Material.AIR) : mmoItem.newBuilder().build(); + return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build()); } @Override - public String getItemID(ItemStack itemStack) { + public String itemID(@NotNull ItemStack itemStack) { NBTItem nbtItem = NBTItem.get(itemStack); if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null; - return nbtItem.getString("MMOITEMS_ITEM_TYPE") + ":" + nbtItem.getString("MMOITEMS_ITEM_ID"); + return nbtItem.getString("MMOITEMS_ITEM_ID"); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MythicMobsItemProvider.java similarity index 67% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MythicMobsItemProvider.java index 1ff645ea4..768055ed4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/MythicMobsItemProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,28 +15,26 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.item; +package net.momirealms.customcrops.bukkit.integration.item; import io.lumine.mythic.bukkit.MythicBukkit; -import net.momirealms.customcrops.api.integration.ItemLibrary; +import net.momirealms.customcrops.api.integration.ItemProvider; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; -public class MythicMobsItemImpl implements ItemLibrary { +public class MythicMobsItemProvider implements ItemProvider { private MythicBukkit mythicBukkit; - public MythicMobsItemImpl() { - this.mythicBukkit = MythicBukkit.inst(); - } - @Override - public String identification() { + public String identifier() { return "MythicMobs"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { if (mythicBukkit == null || mythicBukkit.isClosed()) { this.mythicBukkit = MythicBukkit.inst(); } @@ -44,7 +42,10 @@ public ItemStack buildItem(Player player, String id) { } @Override - public String getItemID(ItemStack itemStack) { + public String itemID(@NotNull ItemStack itemStack) { + if (mythicBukkit == null || mythicBukkit.isClosed()) { + this.mythicBukkit = MythicBukkit.inst(); + } return mythicBukkit.getItemManager().getMythicTypeFromItem(itemStack); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/NeigeItemsItemImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/NeigeItemsItemProvider.java similarity index 66% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/item/NeigeItemsItemImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/NeigeItemsItemProvider.java index 53a56c081..1a366951b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/NeigeItemsItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/NeigeItemsItemProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,29 +15,33 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.item; +package net.momirealms.customcrops.bukkit.integration.item; -import net.momirealms.customcrops.api.integration.ItemLibrary; +import net.momirealms.customcrops.api.integration.ItemProvider; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import pers.neige.neigeitems.item.ItemInfo; import pers.neige.neigeitems.manager.ItemManager; import pers.neige.neigeitems.utils.ItemUtils; -public class NeigeItemsItemImpl implements ItemLibrary { +import java.util.Objects; + +public class NeigeItemsItemProvider implements ItemProvider { @Override - public String identification() { + public String identifier() { return "NeigeItems"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { - return ItemManager.INSTANCE.getItemStack(id, player); + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { + return Objects.requireNonNull(ItemManager.INSTANCE.getItemStack(id, player)); } @Override - public String getItemID(ItemStack itemStack) { + public String itemID(@NotNull ItemStack itemStack) { ItemInfo itemInfo = ItemUtils.isNiItem(itemStack); if (itemInfo != null) { return itemInfo.getId(); diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/ZaphkielItemImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/ZaphkielItemProvider.java similarity index 61% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/item/ZaphkielItemImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/ZaphkielItemProvider.java index 8b79a93a7..cd716e038 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/ZaphkielItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/item/ZaphkielItemProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,36 +15,40 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.item; +package net.momirealms.customcrops.bukkit.integration.item; import ink.ptms.zaphkiel.ZapAPI; import ink.ptms.zaphkiel.Zaphkiel; -import net.momirealms.customcrops.api.integration.ItemLibrary; +import net.momirealms.customcrops.api.integration.ItemProvider; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; -public class ZaphkielItemImpl implements ItemLibrary { +import java.util.Objects; + +public class ZaphkielItemProvider implements ItemProvider { private final ZapAPI zapAPI; - public ZaphkielItemImpl() { + public ZaphkielItemProvider() { this.zapAPI = Zaphkiel.INSTANCE.api(); } @Override - public String identification() { + public String identifier() { return "Zaphkiel"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { - return zapAPI.getItemManager().generateItemStack(id, player); + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { + return Objects.requireNonNull(zapAPI.getItemManager().generateItemStack(id, player)); } @Override - public String getItemID(ItemStack itemStack) { - if (itemStack == null || itemStack.getType() == Material.AIR) return null; + public String itemID(@NotNull ItemStack itemStack) { + if (itemStack.getType() == Material.AIR) return null; return zapAPI.getItemHandler().getItemId(itemStack); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AuraSkillsImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/AuraSkillsLevelerProvider.java similarity index 69% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AuraSkillsImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/AuraSkillsLevelerProvider.java index 8c8023970..b7f3dc78b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AuraSkillsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/AuraSkillsLevelerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,23 +15,29 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.level; +package net.momirealms.customcrops.bukkit.integration.level; import dev.aurelium.auraskills.api.AuraSkillsApi; import dev.aurelium.auraskills.api.registry.NamespacedId; -import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class AuraSkillsImpl implements LevelInterface { +public class AuraSkillsLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public String identifier() { + return "AuraSkills"; + } + + @Override + public void addXp(@NotNull Player player, @NotNull String target, double amount) { AuraSkillsApi.get().getUser(player.getUniqueId()) .addSkillXp(AuraSkillsApi.get().getGlobalRegistry().getSkill(NamespacedId.fromDefault(target)), amount); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return AuraSkillsApi.get().getUser(player.getUniqueId()).getSkillLevel( AuraSkillsApi.get().getGlobalRegistry().getSkill(NamespacedId.fromDefault(target)) ); diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/AureliumSkillsProvider.java similarity index 52% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/AureliumSkillsProvider.java index b9eb4bb43..071f30ca6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/AureliumSkillsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,21 +15,34 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.level; +package net.momirealms.customcrops.bukkit.integration.level; import com.archyx.aureliumskills.api.AureliumAPI; -import net.momirealms.customcrops.api.integration.LevelInterface; +import com.archyx.aureliumskills.leveler.Leveler; +import net.momirealms.customcrops.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class AureliumSkillsImpl implements LevelInterface { +public class AureliumSkillsProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { - AureliumAPI.getPlugin().getLeveler().addXp(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target), amount); + public String identifier() { + return "AureliumSkills"; + } + + private final Leveler leveler; + + public AureliumSkillsProvider() { + leveler = AureliumAPI.getPlugin().getLeveler(); + } + + @Override + public void addXp(@NotNull Player player, @NotNull String target, double amount) { + leveler.addXp(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target), amount); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return AureliumAPI.getSkillLevel(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target)); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoJobsImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/EcoJobsLevelerProvider.java similarity index 69% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoJobsImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/EcoJobsLevelerProvider.java index ea605123a..919336bf4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoJobsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/EcoJobsLevelerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,18 +15,19 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.level; +package net.momirealms.customcrops.bukkit.integration.level; import com.willfp.ecojobs.api.EcoJobsAPI; import com.willfp.ecojobs.jobs.Job; import com.willfp.ecojobs.jobs.Jobs; -import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class EcoJobsImpl implements LevelInterface { +public class EcoJobsLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public void addXp(@NotNull Player player, @NotNull String target, double amount) { for (Job job : EcoJobsAPI.getActiveJobs(player)) { if (job.getId().equals(target)) { EcoJobsAPI.giveJobExperience(player, job, amount); @@ -35,9 +36,14 @@ public void addXp(Player player, String target, double amount) { } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { Job job = Jobs.getByID(target); if (job == null) return 0; return EcoJobsAPI.getJobLevel(player, job); } + + @Override + public String identifier() { + return "EcoJobs"; + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoSkillsImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/EcoSkillsLevelerProvider.java similarity index 67% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoSkillsImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/EcoSkillsLevelerProvider.java index 0ae090fd7..cf662c12c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoSkillsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/EcoSkillsLevelerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,24 +15,30 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.level; +package net.momirealms.customcrops.bukkit.integration.level; import com.willfp.ecoskills.api.EcoSkillsAPI; import com.willfp.ecoskills.skills.Skills; -import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.util.Objects; -public class EcoSkillsImpl implements LevelInterface { +public class EcoSkillsLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public void addXp(@NotNull Player player, @NotNull String target, double amount) { EcoSkillsAPI.gainSkillXP(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target)), amount); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return EcoSkillsAPI.getSkillLevel(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target))); } + + @Override + public String identifier() { + return "EcoSkills"; + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/JobsRebornImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/JobsRebornLevelerProvider.java similarity index 67% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/level/JobsRebornImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/JobsRebornLevelerProvider.java index 0b12ee1d3..7168050ee 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/JobsRebornImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/JobsRebornLevelerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,33 +15,30 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.level; +package net.momirealms.customcrops.bukkit.integration.level; import com.gamingmesh.jobs.Jobs; import com.gamingmesh.jobs.container.Job; import com.gamingmesh.jobs.container.JobProgression; import com.gamingmesh.jobs.container.JobsPlayer; -import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.util.List; -public class JobsRebornImpl implements LevelInterface { +public class JobsRebornLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public void addXp(@NotNull Player player, @NotNull String target, double amount) { JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); - if (jobsPlayer != null) { - List jobs = jobsPlayer.getJobProgression(); - Job job = Jobs.getJob(target); - for (JobProgression progression : jobs) - if (progression.getJob().equals(job)) - progression.addExperience(amount); - } + Job job = Jobs.getJob(target); + if (jobsPlayer != null && jobsPlayer.isInJob(job)) + Jobs.getPlayerManager().addExperience(jobsPlayer, job, amount); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); if (jobsPlayer != null) { List jobs = jobsPlayer.getJobProgression(); @@ -52,4 +49,9 @@ public int getLevel(Player player, String target) { } return 0; } + + @Override + public String identifier() { + return "JobsReborn"; + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/MMOCoreImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/MMOCoreLevelerProvider.java similarity index 68% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/level/MMOCoreImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/MMOCoreLevelerProvider.java index ce1758d62..70a7ce365 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/MMOCoreImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/MMOCoreLevelerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,23 +15,29 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.level; +package net.momirealms.customcrops.bukkit.integration.level; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.experience.EXPSource; -import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class MMOCoreImpl implements LevelInterface { +public class MMOCoreLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public String identifier() { + return "MMOCore"; + } + + @Override + public void addXp(@NotNull Player player, @NotNull String target, double amount) { MMOCore.plugin.professionManager.get(target).giveExperience(PlayerData.get(player), amount, null ,EXPSource.OTHER); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return PlayerData.get(player).getCollectionSkills().getLevel(MMOCore.plugin.professionManager.get(target)); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/McMMOImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/McMMOLevelerProvider.java similarity index 66% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/level/McMMOImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/McMMOLevelerProvider.java index 7bc4f742d..615d9f510 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/McMMOImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/level/McMMOLevelerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,22 +15,28 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.level; +package net.momirealms.customcrops.bukkit.integration.level; import com.gmail.nossr50.api.ExperienceAPI; import com.gmail.nossr50.datatypes.skills.PrimarySkillType; -import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class McMMOImpl implements LevelInterface { +public class McMMOLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public String identifier() { + return "mcMMO"; + } + + @Override + public void addXp(@NotNull Player player, @NotNull String target, double amount) { ExperienceAPI.addRawXP(player, target, (float) amount, "UNKNOWN"); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return ExperienceAPI.getLevel(player, PrimarySkillType.valueOf(target)); } } \ No newline at end of file diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/papi/CustomCropsPapi.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/papi/CustomCropsPapi.java new file mode 100644 index 000000000..91c836ba8 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/papi/CustomCropsPapi.java @@ -0,0 +1,81 @@ +package net.momirealms.customcrops.bukkit.integration.papi; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CustomCropsPapi extends PlaceholderExpansion { + + private final BukkitCustomCropsPlugin plugin; + + public CustomCropsPapi(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + } + + public void load() { + super.register(); + } + + public void unload() { + super.unregister(); + } + + @NotNull + @Override + public String getIdentifier() { + return "customcrops"; + } + + @NotNull + @Override + public String getAuthor() { + return "XiaoMoMi"; + } + + @NotNull + @Override + public String getVersion() { + return "3.6"; + } + + @Nullable + @Override + public String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) { + String[] split = params.split("_", 2); + switch (split[0]) { + case "season" -> { + if (split.length == 1) { + Player player = offlinePlayer.getPlayer(); + if (player == null) + return null; + return plugin.getWorldManager().getSeason(player.getWorld()).translation(); + } else { + try { + return plugin.getWorldManager().getSeason(Bukkit.getWorld(split[1])).translation(); + } catch (NullPointerException e) { + plugin.getPluginLogger().severe("World " + split[1] + " does not exist"); + } + } + } + case "date" -> { + if (split.length == 1) { + Player player = offlinePlayer.getPlayer(); + if (player == null) + return null; + return String.valueOf(plugin.getWorldManager().getDate(player.getWorld())); + } else { + try { + return String.valueOf(plugin.getWorldManager().getDate(Bukkit.getWorld(split[1]))); + } catch (NullPointerException e) { + plugin.getPluginLogger().severe("World " + split[1] + " does not exist"); + } + } + } + } + return null; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/season/AdvancedSeasonsProvider.java similarity index 59% rename from plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java rename to compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/season/AdvancedSeasonsProvider.java index ee32d8ec3..70d9d6b76 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/season/AdvancedSeasonsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,21 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.compatibility.season; +package net.momirealms.customcrops.bukkit.integration.season; import net.advancedplugins.seasons.Core; -import net.momirealms.customcrops.api.integration.SeasonInterface; -import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.api.core.world.Season; +import net.momirealms.customcrops.api.integration.SeasonProvider; import org.bukkit.World; import org.jetbrains.annotations.NotNull; -public class AdvancedSeasonsImpl implements SeasonInterface { +public class AdvancedSeasonsProvider implements SeasonProvider { + @NotNull @Override public Season getSeason(@NotNull World world) { net.advancedplugins.seasons.enums.Season season = Core.getSeasonHandler().getSeason(world); if (season == null) { - return null; + return Season.DISABLE; } return switch (season.getType()) { case SPRING -> Season.SPRING; @@ -40,23 +41,7 @@ public Season getSeason(@NotNull World world) { } @Override - public int getDate(World world) { - return 0; - } - - @Override - public void setSeason(World world, Season season) { - String seasonName = switch (season) { - case AUTUMN -> "FALL"; - case WINTER -> "WINTER"; - case SUMMER -> "SUMMER"; - case SPRING -> "SPRING"; - }; - Core.getSeasonHandler().setSeason(net.advancedplugins.seasons.enums.Season.valueOf(seasonName), world.getName()); - } - - @Override - public void setDate(World world, int date) { - + public String identifier() { + return "AdvancedSeasons"; } } diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/season/RealisticSeasonsProvider.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/season/RealisticSeasonsProvider.java new file mode 100644 index 000000000..b43ee39d9 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/season/RealisticSeasonsProvider.java @@ -0,0 +1,44 @@ +/* + * 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.bukkit.integration.season; + +import me.casperge.realisticseasons.api.SeasonsAPI; +import net.momirealms.customcrops.api.core.world.Season; +import net.momirealms.customcrops.api.integration.SeasonProvider; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +public class RealisticSeasonsProvider implements SeasonProvider { + + @NotNull + @Override + public Season getSeason(@NotNull World world) { + return switch (SeasonsAPI.getInstance().getSeason(world)) { + case WINTER -> Season.WINTER; + case SPRING -> Season.SPRING; + case SUMMER -> Season.SUMMER; + case FALL -> Season.AUTUMN; + case DISABLED, RESTORE -> Season.DISABLE; + }; + } + + @Override + public String identifier() { + return "RealisticSeasons"; + } +} diff --git a/gradle.properties b/gradle.properties index 16d5100f7..9009dbc62 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,46 @@ -#systemProp.socks.proxyHost=127.0.0.1 -#systemProp.socks.proxyPort=7890 +# Project settings +# Rule: [major update].[feature update].[bug fix] +project_version=3.6.0 +config_version=38 +project_group=net.momirealms -#systemProp.http.proxyHost=127.0.0.1 -#systemProp.http.proxyPort=7890 +# Dependency settings +paper_version=1.20.4 +jetbrains_annotations_version=24.0.0 +slf4j_version=2.0.13 +log4j_version=2.23.1 +gson_version=2.10.1 +asm_version=9.7 +asm_commons_version=9.7 +jar_relocator_version=1.7 +adventure_bundle_version=4.17.0 +adventure_platform_version=4.3.3 +sparrow_heart_version=0.38 +cloud_core_version=2.0.0-rc.2 +cloud_services_version=2.0.0-rc.2 +cloud_brigadier_version=2.0.0-beta.9 +cloud_bukkit_version=2.0.0-beta.9 +cloud_paper_version=2.0.0-beta.9 +cloud_minecraft_extras_version=2.0.0-beta.9 +boosted_yaml_version=1.3.7 +byte_buddy_version=1.14.18 +mojang_brigadier_version=1.0.18 +bstats_version=3.0.2 +geantyref_version=1.3.15 +caffeine_version=3.1.8 +rtag_version=6290733498 +exp4j_version=0.4.8 +placeholder_api_version=2.11.6 +anti_grief_version=0.12 +zstd_version=1.5.6-5 +flow_nbt_version=2.0.2 +guava_version=33.2.0-jre +vault_version=1.7 -#systemProp.https.proxyHost=127.0.0.1 -#systemProp.https.proxyPort=7890 +# Proxy settings +systemProp.socks.proxyHost=127.0.0.1 +systemProp.socks.proxyPort=7890 +systemProp.http.proxyHost=127.0.0.1 +systemProp.http.proxyPort=7890 +systemProp.https.proxyHost=127.0.0.1 +systemProp.https.proxyPort=7890 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 269ed9378..f7bde255b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https://services.gradle.org/distributions/gradle-8.10-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/legacy-api/build.gradle.kts b/legacy-api/build.gradle.kts deleted file mode 100644 index 9612fc8fb..000000000 --- a/legacy-api/build.gradle.kts +++ /dev/null @@ -1,16 +0,0 @@ -dependencies { - compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") -} - -tasks.withType { - options.encoding = "UTF-8" - options.release.set(17) -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} \ No newline at end of file diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java deleted file mode 100644 index bafdc53fd..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object; - -import java.io.Serializable; - -@Deprecated -public enum ItemMode implements Serializable { - - ARMOR_STAND, - TRIPWIRE, - ITEM_FRAME, - ITEM_DISPLAY, - NOTE_BLOCK, - CHORUS -} diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/ItemType.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/ItemType.java deleted file mode 100644 index d973fed19..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/ItemType.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object; - -@Deprecated -public enum ItemType { - - GLASS, - POT, - CROP, - SPRINKLER, - SCARECROW, - WATERING_CAN, - UNKNOWN -} diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java deleted file mode 100644 index f80dcf973..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.momirealms.customcrops.api.object; - -import java.io.Serial; -import java.io.Serializable; - -@Deprecated -public class OfflineReplaceTask implements Serializable { - - @Serial - private static final long serialVersionUID = -7700789811612423911L; - - private final String id; - private final ItemType itemType; - private final ItemMode itemMode; - - public OfflineReplaceTask(String id, ItemType itemType, ItemMode itemMode) { - this.id = id; - this.itemMode = itemMode; - this.itemType = itemType; - } - - public String getId() { - return id; - } - - public ItemMode getItemMode() { - return itemMode; - } - - public ItemType getItemType() { - return itemType; - } -} diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/crop/GrowingCrop.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/crop/GrowingCrop.java deleted file mode 100644 index 07290c55e..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/crop/GrowingCrop.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object.crop; - -import java.io.Serial; -import java.io.Serializable; - -@Deprecated -public class GrowingCrop implements Serializable { - - @Serial - private static final long serialVersionUID = 2828962866548871991L; - - private int points; - private final String crop; - - public GrowingCrop(String crop, int points) { - this.points = points; - this.crop = crop; - } - - public int getPoints() { - return points; - } - - public void setPoints(int points) { - this.points = points; - } - - public String getKey() { - return crop; - } -} diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Fertilizer.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Fertilizer.java deleted file mode 100644 index 525178304..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Fertilizer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object.fertilizer; - -import java.io.Serial; -import java.io.Serializable; - -@Deprecated -public class Fertilizer implements Serializable { - - @Serial - private static final long serialVersionUID = -7593869247329159078L; - - private final String key; - private final int times; - - public Fertilizer(String key, int times) { - this.key = key; - this.times = times; - } - - public String getKey() { - return key; - } - - public int getTimes() { - return times; - } -} diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java deleted file mode 100644 index ecfa1cfe7..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object.pot; - -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; - -import java.io.Serial; -import java.io.Serializable; - -@Deprecated -public class Pot implements Serializable { - - @Serial - private static final long serialVersionUID = -6598493908660891824L; - - private Fertilizer fertilizer; - private int water; - private final String key; - - public Pot(String key, Fertilizer fertilizer, int water) { - this.key = key; - this.fertilizer = fertilizer; - this.water = water; - } - - public Fertilizer getFertilizer() { - return fertilizer; - } - - public int getWater() { - return water; - } - - public String getKey() { - return key; - } -} \ No newline at end of file diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/sprinkler/Sprinkler.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/sprinkler/Sprinkler.java deleted file mode 100644 index 1d8023c56..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/sprinkler/Sprinkler.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object.sprinkler; - -import java.io.Serial; -import java.io.Serializable; - -@Deprecated -public class Sprinkler implements Serializable { - - @Serial - private static final long serialVersionUID = -1994328062935821245L; - - private int water; - private final String key; - - public Sprinkler(String key, int water) { - this.water = water; - this.key = key; - } - - public int getWater() { - return water; - } - - public void setWater(int water) { - this.water = water; - } - - public String getKey() { - return key; - } -} diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java deleted file mode 100644 index d18a8e267..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object.world; - -import net.momirealms.customcrops.api.object.OfflineReplaceTask; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.sprinkler.Sprinkler; - -import java.io.Serial; -import java.io.Serializable; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -@Deprecated -public class CCChunk implements Serializable { - - @Serial - private static final long serialVersionUID = 5300805317167684402L; - - private final ConcurrentHashMap growingCropMap; - private final ConcurrentHashMap potMap; - private final ConcurrentHashMap sprinklerMap; - private ConcurrentHashMap replaceTaskMap; - private final Set greenhouseSet; - private final Set scarecrowSet; - - public CCChunk() { - this.growingCropMap = new ConcurrentHashMap<>(64); - this.potMap = new ConcurrentHashMap<>(64); - this.sprinklerMap = new ConcurrentHashMap<>(16); - this.greenhouseSet = Collections.synchronizedSet(new HashSet<>(64)); - this.scarecrowSet = Collections.synchronizedSet(new HashSet<>(4)); - this.replaceTaskMap = new ConcurrentHashMap<>(64); - } - - public ConcurrentHashMap getGrowingCropMap() { - return growingCropMap; - } - - public ConcurrentHashMap getPotMap() { - return potMap; - } - - public ConcurrentHashMap getSprinklerMap() { - return sprinklerMap; - } - - public ConcurrentHashMap getReplaceTaskMap() { - return replaceTaskMap; - } - - public Set getGreenhouseSet() { - return greenhouseSet; - } - - public Set getScarecrowSet() { - return scarecrowSet; - } -} \ No newline at end of file diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/ChunkCoordinate.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/ChunkCoordinate.java deleted file mode 100644 index e3d985ca9..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/ChunkCoordinate.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object.world; - -import java.io.Serial; -import java.io.Serializable; - -@Deprecated -public class ChunkCoordinate implements Serializable { - - @Serial - private static final long serialVersionUID = 8143293419668383618L; - - private final int x; - private final int z; - - public ChunkCoordinate(int x, int z) { - this.x = x; - this.z = z; - } - - public int getX() { - return x; - } - - public int getZ() { - return z; - } - - public String getFileName() { - return x + "," + z; - } - - @Override - public int hashCode() { - long combined = (long) x << 32 | z; - return Long.hashCode(combined); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ChunkCoordinate other = (ChunkCoordinate) obj; - if (this.x != other.x) { - return false; - } - if (this.z != other.z) { - return false; - } - return true; - } -} diff --git a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/SimpleLocation.java b/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/SimpleLocation.java deleted file mode 100644 index 6a11d2626..000000000 --- a/legacy-api/src/main/java/net/momirealms/customcrops/api/object/world/SimpleLocation.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.object.world; - -import java.io.Serial; -import java.io.Serializable; -import java.util.Objects; - -@Deprecated -public class SimpleLocation implements Serializable { - - @Serial - private static final long serialVersionUID = -1288860694388882412L; - - private final int x; - private final int y; - private final int z; - private final String worldName; - - public SimpleLocation(String worldName, int x, int y, int z){ - this.worldName = worldName; - this.x = x; - this.y = y; - this.z = z; - } - - public int getX() { - return x; - } - - public int getZ() { - return z; - } - - public int getY() { - return y; - } - - public String getWorldName() { - return worldName; - } - - public ChunkCoordinate getChunkCoordinate() { - return new ChunkCoordinate(x >> 4, z >> 4); - } - - public SimpleLocation add(int x, int y, int z) { - return new SimpleLocation(worldName, this.x + x, this.y + y, this.z + z); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final SimpleLocation other = (SimpleLocation) obj; - if (!Objects.equals(worldName, other.getWorldName())) { - return false; - } - if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) { - return false; - } - if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) { - return false; - } - if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int hash = 3; - //hash = 19 * hash + (worldName != null ? worldName.hashCode() : 0); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32)); - return hash; - } - - @Override - public String toString() { - return "[" + worldName + "," + x + "," + y + "," + z + "]"; - } - - public SimpleLocation copy() { - return new SimpleLocation(worldName, x, y, z); - } -} \ No newline at end of file diff --git a/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java b/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java deleted file mode 100644 index 57fc32d85..000000000 --- a/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom.oraxen; - -import io.th0rgal.oraxen.api.events.custom_block.noteblock.OraxenNoteBlockBreakEvent; -import io.th0rgal.oraxen.api.events.custom_block.noteblock.OraxenNoteBlockPlaceEvent; -import io.th0rgal.oraxen.api.events.custom_block.stringblock.OraxenStringBlockBreakEvent; -import io.th0rgal.oraxen.api.events.custom_block.stringblock.OraxenStringBlockPlaceEvent; -import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; -import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; -import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; -import net.momirealms.customcrops.api.manager.ItemManager; -import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; -import net.momirealms.customcrops.api.util.LocationUtils; -import org.bukkit.event.EventHandler; - -public class OraxenListener extends AbstractCustomListener { - - public OraxenListener(ItemManager itemManager) { - super(itemManager); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCustomNoteBlock(OraxenNoteBlockBreakEvent event) { - this.itemManager.handlePlayerBreakBlock( - event.getPlayer(), - event.getBlock(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCustomStringBlock(OraxenStringBlockBreakEvent event) { - this.itemManager.handlePlayerBreakBlock( - event.getPlayer(), - event.getBlock(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceCustomBlock(OraxenNoteBlockPlaceEvent event) { - super.onPlaceBlock( - event.getPlayer(), - event.getBlock(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceCustomBlock(OraxenStringBlockPlaceEvent event) { - super.onPlaceBlock( - event.getPlayer(), - event.getBlock(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceFurniture(OraxenFurniturePlaceEvent event) { - super.onPlaceFurniture( - event.getPlayer(), - event.getBlock().getLocation(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakFurniture(OraxenFurnitureBreakEvent event) { - super.onBreakFurniture( - event.getPlayer(), - LocationUtils.toBlockLocation(event.getBaseEntity().getLocation()), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onInteractFurniture(OraxenFurnitureInteractEvent event) { - super.onInteractFurniture( - event.player(), - LocationUtils.toBlockLocation(event.baseEntity().getLocation()), - event.mechanic().getItemID(), - event.baseEntity(), - event - ); - } -} diff --git a/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java b/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java deleted file mode 100644 index c0b52ece6..000000000 --- a/oraxen-j21/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom.oraxen; - -import io.th0rgal.oraxen.api.OraxenBlocks; -import io.th0rgal.oraxen.api.OraxenFurniture; -import io.th0rgal.oraxen.api.OraxenItems; -import io.th0rgal.oraxen.items.ItemBuilder; -import io.th0rgal.oraxen.mechanics.Mechanic; -import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; -import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Rotation; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Entity; -import org.bukkit.inventory.ItemStack; - -public class OraxenProvider implements CustomProvider { - - @Override - public boolean removeBlock(Location location) { - Block block = location.getBlock(); - if (block.getType() == Material.AIR) { - return false; - } - block.setType(Material.AIR); - return true; - } - - @Override - public void placeCustomBlock(Location location, String id) { - OraxenBlocks.place(id, location); - } - - @Override - public Entity placeFurniture(Location location, String id) { - Entity entity = OraxenFurniture.place(id, location, Rotation.NONE, BlockFace.UP); - if (entity == null) { - LogUtils.warn("Furniture(" + id +") doesn't exist in Oraxen configs. Please double check if that furniture exists."); - } - return entity; - } - - @Override - public void removeFurniture(Entity entity) { - OraxenFurniture.remove(entity, null); - } - - @Override - public String getBlockID(Block block) { - Mechanic mechanic = OraxenBlocks.getCustomBlockMechanic(block.getLocation()); - if (mechanic == null) { - return block.getType().name(); - } - return mechanic.getItemID(); - } - - @Override - public String getItemID(ItemStack itemStack) { - return OraxenItems.getIdByItem(itemStack); - } - - @Override - public ItemStack getItemStack(String id) { - if (id == null) return new ItemStack(Material.AIR); - ItemBuilder builder = OraxenItems.getItemById(id); - if (builder == null) { - return null; - } - return builder.build(); - } - - @Override - public String getEntityID(Entity entity) { - FurnitureMechanic mechanic = OraxenFurniture.getFurnitureMechanic(entity); - if (mechanic == null) { - return entity.getType().name(); - } - return mechanic.getItemID(); - } - - @Override - public boolean isFurniture(Entity entity) { - return OraxenFurniture.isFurniture(entity); - } -} diff --git a/oraxen-legacy/.gitignore b/oraxen-legacy/.gitignore deleted file mode 100644 index b63da4551..000000000 --- a/oraxen-legacy/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java deleted file mode 100644 index f9343a41d..000000000 --- a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenListener.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom.oraxenlegacy; - -import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; -import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; -import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; -import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockBreakEvent; -import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockPlaceEvent; -import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockBreakEvent; -import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockPlaceEvent; -import net.momirealms.customcrops.api.manager.ItemManager; -import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; -import net.momirealms.customcrops.api.util.LocationUtils; -import org.bukkit.event.EventHandler; - -public class LegacyOraxenListener extends AbstractCustomListener { - - public LegacyOraxenListener(ItemManager itemManager) { - super(itemManager); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCustomNoteBlock(OraxenNoteBlockBreakEvent event) { - this.itemManager.handlePlayerBreakBlock( - event.getPlayer(), - event.getBlock(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCustomStringBlock(OraxenStringBlockBreakEvent event) { - this.itemManager.handlePlayerBreakBlock( - event.getPlayer(), - event.getBlock(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceCustomBlock(OraxenNoteBlockPlaceEvent event) { - super.onPlaceBlock( - event.getPlayer(), - event.getBlock(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceCustomBlock(OraxenStringBlockPlaceEvent event) { - super.onPlaceBlock( - event.getPlayer(), - event.getBlock(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceFurniture(OraxenFurniturePlaceEvent event) { - super.onPlaceFurniture( - event.getPlayer(), - event.getBlock().getLocation(), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakFurniture(OraxenFurnitureBreakEvent event) { - super.onBreakFurniture( - event.getPlayer(), - LocationUtils.toBlockLocation(event.getBaseEntity().getLocation()), - event.getMechanic().getItemID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onInteractFurniture(OraxenFurnitureInteractEvent event) { - super.onInteractFurniture( - event.getPlayer(), - LocationUtils.toBlockLocation(event.getBaseEntity().getLocation()), - event.getMechanic().getItemID(), - event.getBaseEntity(), - event - ); - } -} diff --git a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java b/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java deleted file mode 100644 index b66c20de7..000000000 --- a/oraxen-legacy/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxenlegacy/LegacyOraxenProvider.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom.oraxenlegacy; - -import io.th0rgal.oraxen.api.OraxenBlocks; -import io.th0rgal.oraxen.api.OraxenFurniture; -import io.th0rgal.oraxen.api.OraxenItems; -import io.th0rgal.oraxen.items.ItemBuilder; -import io.th0rgal.oraxen.mechanics.Mechanic; -import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; -import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Rotation; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Entity; -import org.bukkit.inventory.ItemStack; - -public class LegacyOraxenProvider implements CustomProvider { - - @Override - public boolean removeBlock(Location location) { - Block block = location.getBlock(); - if (block.getType() == Material.AIR) { - return false; - } - block.setType(Material.AIR); - return true; - } - - @Override - public void placeCustomBlock(Location location, String id) { - OraxenBlocks.place(id, location); - } - - @Override - public Entity placeFurniture(Location location, String id) { - Entity entity = OraxenFurniture.place(id, location, Rotation.NONE, BlockFace.UP); - if (entity == null) { - LogUtils.warn("Furniture(" + id +") doesn't exist in Oraxen configs. Please double check if that furniture exists."); - } - return entity; - } - - @Override - public void removeFurniture(Entity entity) { - OraxenFurniture.remove(entity, null); - } - - @Override - public String getBlockID(Block block) { - Mechanic mechanic = OraxenBlocks.getOraxenBlock(block.getLocation()); - if (mechanic == null) { - return block.getType().name(); - } - return mechanic.getItemID(); - } - - @Override - public String getItemID(ItemStack itemStack) { - return OraxenItems.getIdByItem(itemStack); - } - - @Override - public ItemStack getItemStack(String id) { - if (id == null) return new ItemStack(Material.AIR); - ItemBuilder builder = OraxenItems.getItemById(id); - if (builder == null) { - return null; - } - return builder.build(); - } - - @Override - public String getEntityID(Entity entity) { - FurnitureMechanic mechanic = OraxenFurniture.getFurnitureMechanic(entity); - if (mechanic == null) { - return entity.getType().name(); - } - return mechanic.getItemID(); - } - - @Override - public boolean isFurniture(Entity entity) { - return OraxenFurniture.isFurniture(entity); - } -} diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 4ee562599..d6d93ff45 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,75 +1,56 @@ -dependencies { - // Platform - compileOnly("dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT") - compileOnly("com.infernalsuite.aswm:api:1.20.4-R0.1-SNAPSHOT") - - // Command - compileOnly("dev.jorel:commandapi-bukkit-core:9.5.3") - - // Common hooks - compileOnly("me.clip:placeholderapi:2.11.6") - compileOnly("com.comphenix.protocol:ProtocolLib:5.1.0") - compileOnly("com.github.MilkBowl:VaultAPI:1.7") - - // Utils - compileOnly("dev.dejvokep:boosted-yaml:1.3.6") - compileOnly("commons-io:commons-io:2.15.1") - compileOnly("com.google.code.gson:gson:2.10.1") - compileOnly("net.objecthunter:exp4j:0.4.8") - - // eco - compileOnly("com.willfp:eco:6.67.2") - compileOnly("com.willfp:EcoJobs:3.47.1") - compileOnly("com.willfp:EcoSkills:3.21.0") - compileOnly("com.willfp:libreforge:4.48.1") +val commitID: String by project - compileOnly("net.Indyuce:MMOCore-API:1.12-SNAPSHOT") - compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.23") - - compileOnly("com.github.Zrips:Jobs:v4.17.2") - compileOnly("dev.aurelium:auraskills-api-bukkit:2.1.2") - - // Items - compileOnly("com.github.LoneDev6:api-itemsadder:3.6.2-beta-r3-b") - compileOnly("pers.neige.neigeitems:NeigeItems:1.16.24") - compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT") - compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT") - compileOnly("io.lumine:Mythic-Dist:5.6.1") - compileOnly("io.lumine:MythicCrucible-Dist:2.1.0-SNAPSHOT") - - // Quests - compileOnly("org.betonquest:betonquest:2.1.3") +plugins { + id("io.github.goooler.shadow") version "8.1.8" +} - compileOnly(files("libs/BattlePass-4.0.6-api.jar")) - compileOnly(files("libs/ClueScrolls-api.jar")) - compileOnly(files("libs/AdvancedSeasons-API.jar")) - compileOnly(files("libs/zaphkiel-2.0.24.jar")) - compileOnly(files("libs/mcMMO-api.jar")) - compileOnly(files("libs/RealisticSeasons-api.jar")) - compileOnly("org.bstats:bstats-bukkit:3.0.2") +repositories { + mavenCentral() + maven("https://repo.rapture.pw/repository/maven-releases/") + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://jitpack.io/") + maven("https://oss.sonatype.org/content/repositories/snapshots") + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi +} - implementation(project(":api")) - implementation(project(":oraxen-legacy")) - implementation(project(":oraxen-j21")) - implementation(project(":legacy-api")) +dependencies { + // Platform + compileOnly("dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT") + implementation(project(":api")) { + exclude("dev.dejvokep", "boosted-yaml") + } + implementation(project(":common")) + implementation(project(":compatibility")) + implementation(project(":compatibility-oraxen-r1")) + implementation(project(":compatibility-oraxen-r2")) + implementation(project(":compatibility-itemsadder-r1")) + implementation(project(":compatibility-asp-r1")) - implementation("net.kyori:adventure-api:4.17.0") - implementation("net.kyori:adventure-platform-bukkit:4.3.3") - implementation("net.kyori:adventure-text-minimessage:4.17.0") - implementation("net.kyori:adventure-text-serializer-legacy:4.17.0") - implementation("com.github.Xiao-MoMi:AntiGriefLib:0.12") - implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.35") - implementation("com.flowpowered:flow-nbt:2.0.2") - implementation("com.saicone.rtag:rtag:1.5.5") - implementation("com.saicone.rtag:rtag-item:1.5.5") - implementation("com.github.luben:zstd-jni:1.5.6-4") + implementation("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") + implementation("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}") + implementation("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}") + implementation("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") { + exclude("com.google.code.gson", "gson") + } + implementation("net.kyori:adventure-text-serializer-legacy:${rootProject.properties["anti_grief_version"]}") + implementation("com.github.Xiao-MoMi:AntiGriefLib:${rootProject.properties["anti_grief_version"]}") + implementation("com.github.Xiao-MoMi:Sparrow-Heart:${rootProject.properties["sparrow_heart_version"]}") + implementation("com.saicone.rtag:rtag:${rootProject.properties["rtag_version"]}") + implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}") + implementation("com.flowpowered:flow-nbt:${rootProject.properties["flow_nbt_version"]}") // do not relocate for compatibility with SWM + compileOnly("org.incendo:cloud-core:${rootProject.properties["cloud_core_version"]}") + compileOnly("org.incendo:cloud-minecraft-extras:${rootProject.properties["cloud_minecraft_extras_version"]}") + compileOnly("org.incendo:cloud-paper:${rootProject.properties["cloud_paper_version"]}") + compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + compileOnly("org.bstats:bstats-bukkit:${rootProject.properties["bstats_version"]}") + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") } tasks { shadowJar { - relocate ("de.tr7zw.changeme", "net.momirealms.customcrops.libraries.changeme") - relocate ("dev.jorel.commandapi", "net.momirealms.customcrops.libraries.commandapi") + archiveFileName = "CustomCrops-${rootProject.properties["project_version"]}.jar" + destinationDirectory.set(file("$rootDir/target")) relocate ("net.kyori", "net.momirealms.customcrops.libraries") relocate ("org.objenesis", "net.momirealms.customcrops.libraries.objenesis") relocate ("org.bstats", "net.momirealms.customcrops.libraries.bstats") @@ -78,12 +59,13 @@ tasks { relocate ("net.momirealms.antigrieflib", "net.momirealms.customcrops.libraries.antigrieflib") relocate ("net.objecthunter.exp4j", "net.momirealms.customcrops.libraries.exp4j") relocate ("com.saicone.rtag", "net.momirealms.customcrops.libraries.rtag") + relocate ("com.github.benmanes.caffeine", "net.momirealms.customcrops.libraries.caffeine") + relocate("org.incendo", "net.momirealms.customcrops.libraries") } } -tasks.withType { - options.encoding = "UTF-8" - options.release.set(17) +artifacts { + archives(tasks.shadowJar) } java { @@ -92,4 +74,10 @@ java { toolchain { languageVersion = JavaLanguageVersion.of(17) } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) } \ No newline at end of file diff --git a/plugin/libs/Sparrow-Heart-0.19.jar b/plugin/libs/Sparrow-Heart-0.19.jar deleted file mode 100644 index 669b9bb14c5f4391db2e7e6dc84ec93710b97d9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177287 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
{r*6&Fn8()w(Z?r8ykQ)_kh;d55{>Kh^D1SH zfOH=CYla?;^tyGlQ7GXHc&b=cRm+%UCkN<@8S!t%RVk)gsrL}{^p+c`qBrr?;P_#M z9xTi(x3Is(+SnM?3q9}Gf!?G!t@A91SWI!0A3(CoNDPUNlI0Zan>YwMx@S1Sw|hqn;zYVhtC6}S)|H61fZa|a^hVsO-8HG9#IOEy*Z zUYBvP3`Egwn#e*)MThIBM52@VD-Z`2HTJ2kXA`8gQbo%xjJmRo0H%FJQkp6b-A1Py z!P>&ec4gDW_t3<)AGuVPI&A2DQ?03c3;3os$npfH9+jqh5NWgWj4W#zq(cXwedZsW zWrcynDpC||^7V*_U(0A)NW-#1iR%Gj85eIWg*MQ?ntvF=C>*sg*>~4buI{gre+TU| zu6t?DynOFu{Hf0nRry74NzQ9pcxj^h*gG z*ht^bv3=DZA!7$8ePN1Y&~+{ND)A|x)U81ZS;YN&&ko#v8Q)%YM$Ne(2S%TTgS{FU z<0Q^O0jQZpq*N=HV>V|Mc^WS27ou7`e*q7?#7P1}GKwh7Fw}m%dKyP+?awas8*@1( zQj*h~VGXcUg*@7|T_5Go5Oeqm6L1}kqZ=8HLmQd z)53iXF(Crv6C$nsT{Rg#ovN3>Cds=4Kh8ZGP)0-RyQU3;&wG=nr_yYzg$i>8{E7;- zpf@)kE`|xH(#rNcWN(?HrEht~=FUe6bmUu^Y1Mq;KbE_Sxr<%(E}>34Pdc7sB_YU6 z^*(@1L}XgQ4)cysH+x%)XSpM&T1QEIcuEBow{QD?o69Mm&`0lx@X35k!j%eJ3rb}_ zv>RWmE^;6YDaO3~GGPNHr%6@4&!$_DP6zXYtiBg#QE~<|kGnoIl8AK8yPghIah&G4 zjM{ft%IH!fJdH6FAI_CjVkugdoG2QG2}&dHL@l}9#^+m9(72b6!c%J~)ko;**pyK5 z3Zw-cDH59y8LZzGu>x05*UkGDEF$c|_goi4MabxBSM_3BJN`{)xgK=f?O3 zQ|3{)pjQ3>>h(PI#?fm4uibU`TDRFdP2_ET2ydTlK9%#fGp0o%)AI#$`R;=e=cZRvki1f@MP8!|7 z%RH(*%1pT>Fy9rsi#GESeRWl-ArO0-bnZ}gMZD7uo4&&rV5~wU%f-1bS}@7}T;ib0 zu^;aeFsV#J_f4LPL@)PjRkNJGR(`4Bn30+nR#J1OGt-l?bKGuojZbW6+aKuz#gW8= zMRRQ6TC-{fyzI&yhCgh7Huq-!#8U^yIM-r441)}CWWG!wYn?>$(j;tzVx6c$I>u(- zg`_Z}E46A@PT@znK@qExxXPwFS-Gx~F(NVB=&gotVQO74Gl~;(cJ$XfGl@nn-HA34 z`*9of2|QOOrtcZkuo+Z)_O@&1zx{B#JC@<5Xy1q>|518XM^s0YQ1V?hGQwOIN8Cg9 z4SES_&b~u*Xz*U?0ZXaBe)W$eku~7K1;?{x3sPzSnqsmcOAC|^3yc2jDCH3CX1@qk zmeP?qOc>v^9)=EvH>&nS+8D_vPV&vhcF2#4h>|fE%_XBPTHGZQ5-SX^YJ97bRt=2P zd^AfqZECw`=1e&DWXFNPi-HSnh3E zyQ+o-emTvPZAL^bmcF=@)}B$VdNtH>1Xy)Ga|U*!I=H%lVO!l#0%7i}goI?Pb12HBvw zWg;$lPyNF!qCqd(4S8lperF$L_##j*T0LlAj+-lM-1F9gvy1~IpO_=*m2gV5NZJwS#x9@#nOUTUgx*!~x z2OC+%2B@O*^)g!$g$$mCY4C0Ebi)zL#9{T>g{ClFmMpzq5<_t17zu zj%1)8?bXgG-Ew|2lFVr51e69d&P(&WFI78w@W4>7~>j)iLBs z^Ht!(W*R0$xC}^Ln-kqkR`MLeL*(q;=B2}FxAga7W|Gr1WaHJ(5FSEy857c!-L+Xo z-Sf%LQAs|bcuB!yIf|V0OTVGobQL6roi`ul18%`J%1Zb5?Wy2fM#&}WcL+q+HH2>WzR?$|p zUecVx=`#bNB`442DL-c_SM*}`rCTWFo9ib!WoGcQdVc6!Am$&Lt>Y_w#SBJRkj~S! zG=;WD=5@8b6<;mmE(&)D&v9oA>y%?kU){)Ut32fr{ur8x;0zi$yKeFri9NXqIJ||w z&CM>oiM|*1bHHT};_x_@zCZEJ{PlT0iR7qk0M?ri6q7_Gb3h zu6@1IHHu?YCyu42vI4>2{Vkco0OuQe`ho)CCc-^EuUH2WYRdKj6m`|eRg`7z(ghW( zFZ+b1dz*ABS>r0^Qt+kwTRG~>9w(U1T}zA74E>3@^oGdveY+5^6kMCkmfR~j%J(?t z0S`vW{AweLdyp}KJ~7-(qy~tPYi;KF&<-{=kW_0@Q}57v1h@4_n^#N13ga9$C>(+c ze9JSVfyc>9yYeF^Y<=)&3(m%KG*4W?R!2nN~m#o{-v%B(~Myql#Le2}JH z?3Jme{OU=!aIF=^6zhB44>OUf^ z(;nN86!G0d=J4H<=2SPkxI(+F4BCX5D{Rel*c+R&OK$FUKmmi|@f9ArQe19I5!;-B z%H7n3mzrm_`$M|?Yt8j+3t-{$b_ zL%)Mv9d9c0a3F7tWD?2=ZoR&NUOgt29e^JrNOOQ4TKB&}?3eh;<5I6TlzGn|$iR+% ztCHpudHxZwP2Zwg^vr;UQTz{n{P5M>h8uLr{nM?hu2FKu@cr3P|5w`dc zE;=AELsxGz({=)7`9tyM9Izm}csh59>D{M6Ph<=jpOW0uSvIYipC8|Qu47qMudgjl z`_0}H%Lh@WU)+;F!ZI`X#+;eo*jkXqYb##4nf4sv=R01#HOyH3NH+ZHhNO zGdJyebN?y25Wf#8RvR+znz-yjH;@Wt?`q^$AD1I-3&G(Gc?isocw!=S)ElarrZb+p2zzNjCEZbt?4bBDSK|#A>asGRlJ2$e zl}1**MXwheWM7t#c`j5>evB~6$CQ56rV>7UjZshmH!~Jf!tL*Jy!Bx)@1wvq43Af; zdRG|oV16m3a|sZ_0+%20Mxh%0{BdyiOB@}dGUjSC)2Il-+zeKvfd{;s;9KPl3OF5Q ztnRAgg$!=Gtq+Mb-}C%ZP|YMclf|VZG;_0c?`yQfsrl2p@X>olwN>Y1pMpqu;7C8! z+60alG~v)JGdis`)8Y|1o#|sE$;2UPCxk42dYsUc18z~yGBB}RizLPoVdj^caY7OoJXV?Kd> zn9q#B!O+E(uh@LozZXT*IFlvBOy5{DLtXF_iIxI2n+An!HVi<>+>N7eacXfdQ zA!&KXvqpsCpZTINwX5*DF|>2=^i-bGpw3P^?X-?`bHS}1 zhhH037f`<^*!X7tuH%NLA}00S>jymcfkYePW?zqEYvyK+HdeQJ}orO*R`1(M~@uJ1#I&7 zysuc$M>&Z>=ZV?vz^f#X@j^f8b}0lxcM0{Io_4KguIpHc>eP#>SGIDZ2pa1 zu>#2}1nS~nN>$JVf>}vgzCQPd>*`WJNq}wrXcvf!k3Kv@N7yN0`${hgMOCV;L4d%d zL{VccvFcFa>WcUhk&T~{uAEBjQ%uR2gfJJKPrS|130*kcWQ5`vezaMD)rt~5LU z`jPm2OQU!6Dfq(?7X;x^dha6@Mf#N#w2O!AE?=6Z&u|yLX`C?|^qw}8T07BFx9>TL z^A03s#l@T#@Y8LLW5rug&8Cj;I~+J8zAi8lpQ%PRbR2PTd*s+X3oQW+;yQs- zf`Xg^WQdEh**3A1ATjqcpf~!gC_y?=s^o?v&(dQ?#y&U(r~(?qL2}@u^~-2!ZO_@7 zgi9;+d`m(Ox%xuBiEpaq+$p#IhKy#dEIBUfRNfI|Z-SjQWF%b9U~}T6Nhk#llhy!( zX@IaS9prdJyB=A{0~6W4SN9Z#wmu-Hf3sq~hWhg$Bau1i8|cH82#ZZ^HioL=Lt&Q^ zX~ljF;^z8A_40(nEa(upVLzzL-iD~8bgFDpxhZCeTo6t~>twK`0h#gmUGd43!;e39 z6Od4GU3FE+%Z@xkKClWIZS#i`(NS*QjOHinUVpHI?hq^@T+5!j<4XoW6F)APpX9 z_&r?FB^&%zp%R%Y^}>}$9)PvLmIt`I%Y_4(;wg(0>Du8*!a^g4wCG$F#KR|+@7olhu5QYjg5d;;)qg)PSz%mU{T1ZQ1YuJ` z$tE3Op)Y(((7u#lsX4^v$|zoV$*3&5QDJ~yqBctO%(ieW=1`qNhH$A_ImgUZtZ#C% z_(HkCEbD`C4o)w9hOY9**jd`@7!)!n?7mI>YLQLCQ|X1T+>rE6Z?K5*sm$Y-C`vIp z?diYS7kT%vLO<%b1{}q$mg#$Sq(IKok*&~7Ut(wXZ}$~Wxc2N~6dP|4o9H&+Vx#mN z*`ls>_cjNf>Jx9|c5fdkeonLWHbQn#dzkq8W!M1C$!SxJg5&hk>;^sCu^-RHF0vMa z=f)b+v31I6)R4oZx*OBERHApJP@boPfd&tEd6FDg?#9;zYcD!hmMR(aV4u7oCqKi; zPUD!PjSM#1-v@y#Yp)FRNTq%|Sh7b+t$28d=(IeH|U#M2kCp1%}q|XVmA$WHWl7))J{pn-p9sUW@n9Y-o^pM>S7tVLl+X_btKG@!Q^~Go27U6eM?UYuFoN z@1FBw5epdMH@WCd(36Kb^^4j0Nrqy(lJ$}k-3SZJH)uDEc}bpC>)3gbRcM>Yux)p0 zHwu_Xgqj}OevJx`&ARfn_cJP5lWJsi z{bfUl`ANc3Nxqz~D{Cf`@GU)mss734XcV~tjE2yg8#30fr8&Yaelf?yJEBnriH=k^ zH>?3CoISlBb$o`4V@Y)`Beq^!e5#K#(@Ax8;|yXVc$6#lVl%@ulw4u*HlYLXT4sJ` z*gPUPPj!K#Ryr{rjGrU#dx=x*lD~YGvb1hc*`Ig5zMr|=I{JPF%!#}1t*tl&KApNj zR;xP;lZ8y1O*&A=QF0!wDx+UNGhxtzr;AcMiC6jjz^9wiZ_OnxvKT5S7M)FWnIc`c zbySOSP(wH(RMsrXa;Pp+6_$4-t>_=no~q@RujQ4$!z)~PSGZt^v%)iQGaRMXX7qaZ z`60kKRb#?l2HvHCHdJ|&*bor6LDgGelWY=sL6}&i3mR@4&OdFFT~I$^18;Eq!GIMg z;gBE64fp)WO}Rf+B;7EcZ-Ul8M;A_Ppz0H8!qx)49Nfo=*YlCGYt9T&gKa&N{qjNY z_CI~V$b5=v?(^Qv<_ z^!k4930P7D6buRo2m%7=`EF{NB-&OpKwTXW8W4~G;ES2A4V9_6siBpwmWin~m9>SI zm6f?Ym4U97l?|1lsf7uZm9DwAo(7Eu%`bOT|7l$vqyr|PAzaT)cOon#9ArHN5%4;J zmX4suEYa#L4N0#Z#-{z(-`kj<1VD> zE)V?t_}#w1-AK`Ujf&!|R8Heh=F9g-L(b;Q&fj}dTvzdc6xLQi#2^=#zMCzcn2@7N z#OG&iIvyfcj?}QP*sS-(?arAgjHEa4*(zY2?hOjIjc#+!yMqIH*YF^A-@ShN; zH6y-94n?uZawXRDOO686)#Zj^bVxA`|NpwWL{lmh>Vqn*QktTo)%OQ^`WM$*h%`2xReog=G2RG*FSA7564^9$#1a;kU+%VU;d&iB zRG}6Ikl2wu%W5v|9w*+fB7 zm56a=JDgaDbxi6kZxTz23sQ_=Kx5k`%YiXrle`I$cBMhjBq{Y14L-Jxqz@$is~?<5 z0i56jWqtyVc7*zPs?{5C)YT89N%x!ed2j0MA`0KuWHsRE5Dp-LiKwTXa7$@RZ$zc1 zW<#m6Y9&Hd4!TGt>nSA9efS#r(a=Eq-5ARcQ$)`Yuc|^y<{eo2O#U856SO)j%FXUB zQU>eXDGQ2F!|Xh*%0w(AgJhlo$V<|V8gvLP|B!u)=C&K_MQd175Pq{ztJO)HS?XkI z4u$o!bGU=av5;-ifa}%6F_bQO`>Uw81KPOMWv`vLb(k(8RTaPVfRSy}#(PYg=DLhz zdyXW2UJcT*?=HeykV#jFkv#(ep{!J3p$8hZc+=eH(HIgdw6#B9kiRTEx19DPp2m4J zB%TMg4}$4fRgo1gom|D;D)Bl{9fvwHWxr91N$;`(W^{54SVibi@D5m3#6lRB-rR5s zHvJvfh-}ATsf&EFN8_BBg&LtQDCn2yOk7J(*xYJ4?}3kT6>pZ> z^TCV?zFEJ;TQv#OXNQpxKpONS*Sjq7Kr|?)`@Z(gm7oJZbB4h>pTXZ;)>cAU0BJ5E zl55IlL25=On=-Sn^Kjh}1Z^ZCXQRxS#4wkBQCQ*As~lzP9N6|HQrh)r|+=y4A0Idvl%d4MS# z@BCv&*MyMsql5}A=J2rK$l}+~sP7%udE5$e21boz!sSb7e$rO2OmY%Y>)!Gy7=9)ifCHhw2Gnsm%+tB>vQIiIfSVQjmjDMjf;|2*1PZS#I)GPS<83%-r}I8G1Pl zMPvc{!(L!vjhsKRwWPnqcP(uAH9DB)C{bfTkA($vir7-RH09Ke?QHUvwqe&C(W13L z7`dLdOiw(VH_XmnUL}~76zavL$FN;y^z5cW`A`3D6sPmAMsX4K0W|^EautbpA6kdR zvy0S~57?T5IZ`lJ5O)pq$&*X(lG{0@zQy4|)&w`ZS7J&uK&Hg7y1}o^oT%MZzG>W@GpH5{2G>WTzZWM2X=Dl185Xy z9n(fP`EI86xltTTq;#lL)_)qs{qTq;cRVQ59{{UF7E!tYEiut4zc`1!_Q^=cndjli z^_*Qx_Cv3iV|`aBI1z7$q$Gi4p>=o@&A()qlgDz@tDx4oE0*VORc6A_xP*o32RWA# zVn4TRCh|a+!7JZBs~)KIwUw@uU$$gK7j0WAXhh$$4P?rvR;dpr5O62C`lOkyHY4RK zc?DRJ{phvuRwC*ou@IEgdhum_)N4k=6PM z7iWW@X|bf9M=SP7z)okP0?O^ixgjWfP&^X)_#p1R&!p429kf;B$Bf7U<#t7SgbWyH z&*gT~CP_dlG2Rq1jD79T<#zCmPGko}-^@~gr{f?1<#w-TX<=i!4U?c6eakNB+NLKo zS!SL7Rc?2;o1k4^yahHr9g<=DK=Lhf)|TrMO>0G#MrC)gFgmVZQ$c)CdYtX+2mP5< zp!)2#Q~U@XFJvcBI6WVnba_IonQ6`-K4L^uOz$P7)3jwJQ3@lrPopy}k6=ic;@@y* z(s)e`b1+rPKo-!Ui-jW-fXH(Dlo!9yXEiElhh+rxs)Z!dd<>@hR427-Eh3M50+ty+ zhS%SH!7}fNRa?sSH8x}w<%bUQw2-cPN9&AC#}M(!w5EeIbrbp9Wi%Mvjp6n0b;e*< zld(Z-%fqk)%<3BbT~qd&bEo4pNjn?24+IVtQkx{4&PafAg%iZdSH`+MC%PrkLF<^R zPzKpOC#K`?S4hNz*K^x*PTKzIH08Oq)%M7zmg&P|#*$i6t9hW*3 zeHoYg><-qq-{F7JW^G@Uu3$L>U#(rsQ}8`ng9%yyqbV}W4527gUJ^^J7-KKFzed`@O^d*mAW&HVcj z1xfyN{ILxZZ+WNO%R|+oXj1La@otE>wDS=6B9?|vfF8fVAjqKqO6q`67Zm|{91zI! zC&2NX+x_c60Q$l6{7Q^pmWM(}{1er$PXm<7C|02#y|32ea+2~)L49H)9 zb@cuIxS)R%Qp?KJ+{)TU%k1+Dbh)>~ijX-#={Nv`$_0dc*6W(WM_ zr=WQO6ueqieC8(RR=?NrOg5g#s+<5I^94ZjydX1y_-AAicDhz3T8_V``>h+0zo65F z{+o0<&tiUOw9q4wJr!^uAS;;P8m;W-fW@^;bwA7MI@tV)(SECf>V*meq`#@cz);`7 z1n_)0DRRIrDw6Uy3Q;wmTovxXU zxs~JZ1_l>~v_29bM*-kq94~B@@bln;fOwQKF}L~OI>=s7eR=ygb=c@?nf^|~Iwz)$ z3gFMD0r=L-7#aU5Tv-4(z^hCQpQm7HTNB;i%XkjSg3vZYXTadE0|Jocg^VDapONuz zx<8A2aq4nkZGfux09BkXXw(UQmPQ^RN!!rG(B==q{@JHlCd8(yUjqSs09@r?PH9+x z?BXw{w12NL{ZhF9UdGT&-$d8O+)U8i-$@H|DNIH0N8&J-8o4G zilAFTwJHDxJOBgx3x*t~zw;N%=$hyWndt!51g&-X&1?W;EcEgBwgGfCRXo-1^AQCw z!2p;5(7!rV0Dk|dxiDCHVOF$=zp@_`diXF%2eC0lt4htp8wh;23ThDeVi}i2@{b`KKPC0{m|9cebnf2k3udyJwblP{|$F|CKXC$v(N%K zd$ZGTon43M=Oz8gfcTe|( z{sZ*CA?ca4N9F_s8Zcl^zZ)=u{-2lhCj<7^e&>JrQ$)37e&`tGGGM9Bec z2l_kPP5lG(Ke62(Z6EyKnDtivc^Us_+i`sUDbio=wD|Q^{Ezm*`>&+ne}Micw)>-7 iME*;(5dQNr{?vA_084j3K-_?j4#1L1Ng@c~yZ;B9`E8Q` diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java deleted file mode 100644 index 499976854..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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; - -import net.momirealms.antigrieflib.AntiGriefLib; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.CoolDownManager; -import net.momirealms.customcrops.api.util.EventUtils; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.compatibility.IntegrationManagerImpl; -import net.momirealms.customcrops.libraries.classpath.ReflectionClassPathAppender; -import net.momirealms.customcrops.libraries.dependencies.Dependency; -import net.momirealms.customcrops.libraries.dependencies.DependencyManager; -import net.momirealms.customcrops.libraries.dependencies.DependencyManagerImpl; -import net.momirealms.customcrops.manager.*; -import net.momirealms.customcrops.mechanic.action.ActionManagerImpl; -import net.momirealms.customcrops.mechanic.condition.ConditionManagerImpl; -import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; -import net.momirealms.customcrops.mechanic.misc.migrator.Migration; -import net.momirealms.customcrops.mechanic.requirement.RequirementManagerImpl; -import net.momirealms.customcrops.mechanic.world.WorldManagerImpl; -import net.momirealms.customcrops.scheduler.SchedulerImpl; -import org.bstats.bukkit.Metrics; -import org.bukkit.Bukkit; -import org.bukkit.plugin.Plugin; - -import java.util.ArrayList; -import java.util.List; - -public class CustomCropsPluginImpl extends CustomCropsPlugin { - - private DependencyManager dependencyManager; - private PacketManager packetManager; - private CommandManager commandManager; - private HologramManager hologramManager; - - @Override - public void onLoad() { - this.versionManager = new VersionManagerImpl(this); - this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader())); - this.dependencyManager.loadDependencies(new ArrayList<>( - List.of( - Dependency.GSON, - Dependency.EXP4J, - Dependency.SLF4J_API, - Dependency.SLF4J_SIMPLE, - versionManager.isMojmap() ? Dependency.COMMAND_API_MOJMAP : Dependency.COMMAND_API, - Dependency.BOOSTED_YAML, - Dependency.BSTATS_BASE, - Dependency.BSTATS_BUKKIT - ) - )); - } - - @Override - public void onEnable() { - instance = this; - this.adventure = new AdventureManagerImpl(this); - this.scheduler = new SchedulerImpl(this); - this.configManager = new ConfigManagerImpl(this); - this.integrationManager = new IntegrationManagerImpl(this); - this.conditionManager = new ConditionManagerImpl(this); - this.actionManager = new ActionManagerImpl(this); - this.requirementManager = new RequirementManagerImpl(this); - this.coolDownManager = new CoolDownManager(this); - this.worldManager = new WorldManagerImpl(this); - this.itemManager = new ItemManagerImpl(this, - AntiGriefLib.builder(this) - .silentLogs(true) - .ignoreOP(true) - .build() - ); - this.messageManager = new MessageManagerImpl(this); - this.packetManager = new PacketManager(this); - this.commandManager = new CommandManager(this); - this.placeholderManager = new PlaceholderManagerImpl(this); - this.hologramManager = new HologramManager(this); - this.commandManager.init(); - try { - this.integrationManager.init(); - } catch (Exception e) { - e.printStackTrace(); - } - BukkitItemFactory.create(this); - Migration.tryUpdating(); - this.reload(); - if (ConfigManager.metrics()) new Metrics(this, 16593); - if (ConfigManager.checkUpdate()) { - this.versionManager.checkUpdate().thenAccept(result -> { - if (!result) this.getAdventure().sendConsoleMessage("[CustomCrops] You are using the latest version."); - else this.getAdventure().sendConsoleMessage("[CustomCrops] Update is available: https://polymart.org/resource/2625"); - }); - } - } - - @Override - public void onDisable() { - if (this.commandManager != null) this.commandManager.disable(); - if (this.adventure != null) this.adventure.disable(); - if (this.requirementManager != null) this.requirementManager.disable(); - if (this.actionManager != null) this.actionManager.disable(); - if (this.worldManager != null) this.worldManager.disable(); - if (this.itemManager != null) this.itemManager.disable(); - if (this.conditionManager != null) this.conditionManager.disable(); - if (this.coolDownManager != null) this.coolDownManager.disable(); - if (this.placeholderManager != null) this.placeholderManager.disable(); - if (this.scheduler != null) ((SchedulerImpl) scheduler).shutdown(); - instance = null; - } - - @Override - public void reload() { - this.configManager.reload(); - this.messageManager.reload(); - this.itemManager.reload(); - this.worldManager.reload(); - this.actionManager.reload(); - this.requirementManager.reload(); - this.conditionManager.reload(); - this.coolDownManager.reload(); - this.placeholderManager.reload(); - this.hologramManager.reload(); - ((SchedulerImpl) scheduler).reload(); - EventUtils.fireAndForget(new CustomCropsReloadEvent(this)); - } - - @Override - public void debug(String debug) { - if (ConfigManager.debug()) { - LogUtils.info(debug); - } - } - - public DependencyManager getDependencyManager() { - return dependencyManager; - } - - public PacketManager getPacketManager() { - return packetManager; - } - - public HologramManager getHologramManager() { - return hologramManager; - } - - @Override - public boolean isHookedPluginEnabled(String plugin) { - return Bukkit.getPluginManager().isPluginEnabled(plugin); - } - - @Override - public boolean isHookedPluginEnabled(String hooked, String... versionPrefix) { - Plugin p = Bukkit.getPluginManager().getPlugin(hooked); - if (p != null) { - String ver = p.getDescription().getVersion(); - for (String prefix : versionPrefix) { - if (ver.startsWith(prefix)) { - return true; - } - } - } - return false; - } - - @Override - public boolean doesHookedPluginExist(String plugin) { - return Bukkit.getPluginManager().getPlugin(plugin) != null; - } - - @Override - public String getServerVersion() { - return Bukkit.getServer().getBukkitVersion().split("-")[0]; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitBootstrap.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java rename to plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitBootstrap.java index 9125b512e..2ec7fa64e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitBootstrap.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 @@ -15,24 +15,28 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.mechanic.misc.value; +package net.momirealms.customcrops.bukkit; -import net.momirealms.customcrops.api.mechanic.misc.Value; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.entity.Player; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import org.bukkit.plugin.java.JavaPlugin; -import java.util.HashMap; +public class BukkitBootstrap extends JavaPlugin { -public class ExpressionValue implements Value { + private BukkitCustomCropsPlugin plugin; - private final String expression; + @Override + public void onLoad() { + this.plugin = new BukkitCustomCropsPluginImpl(this); + this.plugin.load(); + } - public ExpressionValue(String expression) { - this.expression = expression; + @Override + public void onEnable() { + this.plugin.enable(); } @Override - public double get(Player player) { - return ConfigUtils.getExpressionValue(player, expression, new HashMap<>(0)); + public void onDisable() { + this.plugin.disable(); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java new file mode 100644 index 000000000..852394a39 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -0,0 +1,223 @@ +package net.momirealms.customcrops.bukkit; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.SimpleRegistryAccess; +import net.momirealms.customcrops.api.core.block.*; +import net.momirealms.customcrops.api.core.item.*; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; +import net.momirealms.customcrops.api.misc.cooldown.CoolDownManager; +import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.bukkit.action.BlockActionManager; +import net.momirealms.customcrops.bukkit.action.PlayerActionManager; +import net.momirealms.customcrops.bukkit.command.BukkitCommandManager; +import net.momirealms.customcrops.bukkit.config.BukkitConfigManager; +import net.momirealms.customcrops.bukkit.integration.BukkitIntegrationManager; +import net.momirealms.customcrops.bukkit.item.BukkitItemManager; +import net.momirealms.customcrops.bukkit.misc.HologramManager; +import net.momirealms.customcrops.bukkit.requirement.BlockRequirementManager; +import net.momirealms.customcrops.bukkit.requirement.PlayerRequirementManager; +import net.momirealms.customcrops.bukkit.scheduler.BukkitSchedulerAdapter; +import net.momirealms.customcrops.bukkit.sender.BukkitSenderFactory; +import net.momirealms.customcrops.bukkit.world.BukkitWorldManager; +import net.momirealms.customcrops.common.config.ConfigLoader; +import net.momirealms.customcrops.common.dependency.Dependency; +import net.momirealms.customcrops.common.dependency.DependencyManagerImpl; +import net.momirealms.customcrops.common.helper.VersionHelper; +import net.momirealms.customcrops.common.locale.TranslationManager; +import net.momirealms.customcrops.common.plugin.classpath.ClassPathAppender; +import net.momirealms.customcrops.common.plugin.classpath.ReflectionClassPathAppender; +import net.momirealms.customcrops.common.plugin.feature.Reloadable; +import net.momirealms.customcrops.common.plugin.logging.JavaPluginLogger; +import net.momirealms.customcrops.common.plugin.logging.PluginLogger; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bstats.bukkit.Metrics; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.function.Consumer; + +public class BukkitCustomCropsPluginImpl extends BukkitCustomCropsPlugin { + + private final ClassPathAppender classPathAppender; + private final PluginLogger logger; + private BukkitCommandManager commandManager; + private HologramManager hologramManager; + private Consumer debugger; + private String buildByBit = "%%__BUILTBYBIT__%%"; + private String polymart = "%%__POLYMART__%%"; + private String time = "%%__TIMESTAMP__%%"; + private String user = "%%__USER__%%"; + private String username = "%%__USERNAME__%%"; + + public BukkitCustomCropsPluginImpl(Plugin boostrap) { + super(boostrap); + VersionHelper.init(getServerVersion()); + this.scheduler = new BukkitSchedulerAdapter(this); + this.logger = new JavaPluginLogger(getBoostrap().getLogger()); + this.classPathAppender = new ReflectionClassPathAppender(this); + this.dependencyManager = new DependencyManagerImpl(this); + this.registryAccess = new SimpleRegistryAccess(this); + } + + @Override + public void debug(Object message) { + if (this.debugger != null) + this.debugger.accept(message); + } + + @Override + public InputStream getResourceStream(String filePath) { + return getBoostrap().getResource(filePath); + } + + @Override + public PluginLogger getPluginLogger() { + return logger; + } + + @Override + public ClassPathAppender getClassPathAppender() { + return classPathAppender; + } + + @Override + public Path getDataDirectory() { + return getBoostrap().getDataFolder().toPath().toAbsolutePath(); + } + + @Override + public ConfigLoader getConfigManager() { + return configManager; + } + + @Override + public String getServerVersion() { + return Bukkit.getServer().getBukkitVersion().split("-")[0]; + } + + @SuppressWarnings("deprecation") + @Override + public String getPluginVersion() { + return getBoostrap().getDescription().getVersion(); + } + + @Override + public void load() { + this.dependencyManager.loadDependencies( + List.of( + Dependency.BOOSTED_YAML, + Dependency.BSTATS_BASE, Dependency.BSTATS_BUKKIT, + Dependency.CAFFEINE, + Dependency.GEANTY_REF, + Dependency.CLOUD_CORE, Dependency.CLOUD_SERVICES, Dependency.CLOUD_BUKKIT, Dependency.CLOUD_PAPER, Dependency.CLOUD_BRIGADIER, Dependency.CLOUD_MINECRAFT_EXTRAS, + Dependency.GSON, + Dependency.EXP4J, + Dependency.ZSTD + ) + ); + this.registerDefaultMechanics(); + } + + @Override + public void enable() { + SparrowHeart.getInstance(); + this.configManager = new BukkitConfigManager(this); + super.requirementManagers.put(Player.class, new PlayerRequirementManager(this)); + super.requirementManagers.put(CustomCropsBlockState.class, new BlockRequirementManager(this)); + super.actionManagers.put(Player.class, new PlayerActionManager(this)); + super.actionManagers.put(CustomCropsBlockState.class, new BlockActionManager(this)); + this.translationManager = new TranslationManager(this); + this.senderFactory = new BukkitSenderFactory(this); + this.itemManager = new BukkitItemManager(this); + this.integrationManager = new BukkitIntegrationManager(this); + this.placeholderManager = new BukkitPlaceholderManager(this); + this.coolDownManager = new CoolDownManager(this); + this.worldManager = new BukkitWorldManager(this); + this.hologramManager = new HologramManager(this); + this.commandManager = new BukkitCommandManager(this); + this.commandManager.registerDefaultFeatures(); + + boolean downloadFromPolymart = polymart.equals("1"); + boolean downloadFromBBB = buildByBit.equals("true"); + + this.getScheduler().sync().runLater(() -> { + this.reload(); + ((SimpleRegistryAccess) registryAccess).freeze(); + if (ConfigManager.metrics()) new Metrics((JavaPlugin) getBoostrap(), 16593); + if (ConfigManager.checkUpdate()) { + VersionHelper.UPDATE_CHECKER.apply(this).thenAccept(result -> { + String link; + if (downloadFromPolymart) { + link = "https://polymart.org/resource/2625/"; + } else if (downloadFromBBB) { + link = "https://builtbybit.com/resources/36363/"; + } else { + link = "https://github.com/Xiao-MoMi/Custom-Crops/"; + } + if (!result) { + this.getPluginLogger().info("You are using the latest version."); + } else { + this.getPluginLogger().warn("Update is available: " + link); + } + }); + } + }, 1, null); + } + + @Override + public void disable() { + this.worldManager.disable(); + this.placeholderManager.disable(); + this.hologramManager.disable(); + this.integrationManager.disable(); + this.coolDownManager.disable(); + this.commandManager.unregisterFeatures(); + } + + @Override + public void reload() { + + this.worldManager.unload(); + + this.configManager.reload(); + this.debugger = ConfigManager.debug() ? (s) -> logger.info("[DEBUG] " + s.toString()) : (s) -> {}; + this.coolDownManager.reload(); + this.placeholderManager.reload(); + this.translationManager.reload(); + this.hologramManager.reload(); + + this.actionManagers.values().forEach(Reloadable::reload); + this.requirementManagers.values().forEach(Reloadable::reload); + + this.worldManager.load(); + + EventUtils.fireAndForget(new CustomCropsReloadEvent(this)); + } + + private void registerDefaultMechanics() { + registryAccess.registerFertilizerType(FertilizerType.SPEED_GROW); + registryAccess.registerFertilizerType(FertilizerType.QUALITY); + registryAccess.registerFertilizerType(FertilizerType.SOIL_RETAIN); + registryAccess.registerFertilizerType(FertilizerType.VARIATION); + registryAccess.registerFertilizerType(FertilizerType.YIELD_INCREASE); + + registryAccess.registerBlockMechanic(new CropBlock()); + registryAccess.registerBlockMechanic(new PotBlock()); + registryAccess.registerBlockMechanic(new ScarecrowBlock()); + registryAccess.registerBlockMechanic(new SprinklerBlock()); + registryAccess.registerBlockMechanic(new GreenhouseBlock()); + + registryAccess.registerItemMechanic(new SeedItem()); + registryAccess.registerItemMechanic(new WateringCanItem()); + registryAccess.registerItemMechanic(new FertilizerItem()); + registryAccess.registerItemMechanic(new SprinklerItem()); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java new file mode 100644 index 000000000..a09aed243 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java @@ -0,0 +1,110 @@ +package net.momirealms.customcrops.bukkit.action; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.AbstractActionManager; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.CustomForm; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.FurnitureRotation; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.block.PotBlock; +import net.momirealms.customcrops.api.core.block.VariationData; +import net.momirealms.customcrops.api.core.item.Fertilizer; +import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.world.CustomCropsChunk; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Pos3; +import org.bukkit.Location; + +import java.util.*; + +import static java.util.Objects.requireNonNull; + +public class BlockActionManager extends AbstractActionManager { + + public BlockActionManager(BukkitCustomCropsPlugin plugin) { + super(plugin); + } + + @Override + public void load() { + loadExpansions(CustomCropsBlockState.class); + } + + @Override + protected void registerBuiltInActions() { + super.registerBuiltInActions(); + super.registerBundleAction(CustomCropsBlockState.class); + this.registerVariationAction(); + } + + private void registerVariationAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + boolean ignore = section.getBoolean("ignore-fertilizer", false); + List variationDataList = new ArrayList<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + VariationData variationData = new VariationData( + inner.getString("item"), + CustomForm.valueOf(inner.getString("type", "TripWire").toUpperCase(Locale.ENGLISH)).existenceForm(), + inner.getDouble("chance") + ); + variationDataList.add(variationData); + } + } + VariationData[] variations = variationDataList.toArray(new VariationData[0]); + return context -> { + if (Math.random() > chance) return; + if (!(context.holder().type() instanceof CropBlock cropBlock)) { + return; + } + Fertilizer[] fertilizers = null; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isEmpty()) { + return; + } + Pos3 pos3 = Pos3.from(location); + if (!ignore) { + Pos3 potLocation = pos3.add(0, -1, 0); + Optional chunk = world.get().getChunk(potLocation.toChunkPos()); + if (chunk.isPresent()) { + Optional state = chunk.get().getBlockState(potLocation); + if (state.isPresent()) { + if (state.get().type() instanceof PotBlock potBlock) { + fertilizers = potBlock.fertilizers(state.get()); + } + } + } + } + ArrayList configs = new ArrayList<>(); + if (fertilizers != null) { + for (Fertilizer fertilizer : fertilizers) { + Optional.ofNullable(fertilizer.config()).ifPresent(configs::add); + } + } + for (VariationData variationData : variations) { + double variationChance = variationData.chance(); + for (FertilizerConfig fertilizer : configs) { + variationChance = fertilizer.processVariationChance(variationChance); + } + if (Math.random() < variationChance) { + plugin.getItemManager().remove(location, ExistenceForm.ANY); + world.get().removeBlockState(pos3); + plugin.getItemManager().place(location, variationData.existenceForm(), variationData.id(), FurnitureRotation.random()); + cropBlock.fixOrGetState(world.get(), pos3, variationData.id()); + break; + } + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at variation action which is expected to be `Section`"); + return Action.empty(); + } + }, "variation"); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java new file mode 100644 index 000000000..d6cd17256 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java @@ -0,0 +1,524 @@ +package net.momirealms.customcrops.bukkit.action; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.AbstractActionManager; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.block.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.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.api.util.PlayerUtils; +import net.momirealms.customcrops.bukkit.integration.VaultHook; +import net.momirealms.customcrops.bukkit.misc.HologramManager; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.util.ListUtils; +import net.momirealms.customcrops.common.util.RandomUtils; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.feature.inventory.HandSlot; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.ExperienceOrb; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.*; + +import static java.util.Objects.requireNonNull; + +public class PlayerActionManager extends AbstractActionManager { + + public PlayerActionManager(BukkitCustomCropsPlugin plugin) { + super(plugin); + } + + @Override + public void load() { + loadExpansions(Player.class); + } + + @Override + protected void registerBuiltInActions() { + super.registerBuiltInActions(); + super.registerBundleAction(Player.class); + this.registerPlayerCommandAction(); + this.registerCloseInvAction(); + this.registerActionBarAction(); + this.registerExpAction(); + this.registerFoodAction(); + this.registerItemAction(); + this.registerMoneyAction(); + this.registerPotionAction(); + this.registerSoundAction(); + this.registerPluginExpAction(); + this.registerTitleAction(); + this.registerSwingHandAction(); + this.registerForceTickAction(); + this.registerHologramAction(); + this.registerMessageAction(); + } + + private void registerMessageAction() { + registerAction((args, chance) -> { + List messages = ListUtils.toList(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + List replaced = plugin.getPlaceholderManager().parse(context.holder(), messages, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(context.holder()); + for (String text : replaced) { + audience.sendMessage(AdventureHelper.miniMessage(text)); + } + }; + }, "message"); + registerAction((args, chance) -> { + List messages = ListUtils.toList(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + String random = messages.get(RandomUtils.generateRandomInt(0, messages.size() - 1)); + random = BukkitPlaceholderManager.getInstance().parse(context.holder(), random, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(context.holder()); + audience.sendMessage(AdventureHelper.miniMessage(random)); + }; + }, "random-message"); + } + + private void registerPlayerCommandAction() { + registerAction((args, chance) -> { + List commands = ListUtils.toList(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + List replaced = BukkitPlaceholderManager.getInstance().parse(context.holder(), commands, context.placeholderMap()); + plugin.getScheduler().sync().run(() -> { + for (String text : replaced) { + context.holder().performCommand(text); + } + }, context.holder().getLocation()); + }; + }, "player-command"); + } + + private void registerCloseInvAction() { + registerAction((args, chance) -> context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + context.holder().closeInventory(); + }, "close-inv"); + } + + private void registerActionBarAction() { + registerAction((args, chance) -> { + String text = (String) args; + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Audience audience = plugin.getSenderFactory().getAudience(context.holder()); + Component component = AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(context.holder(), text, context.placeholderMap())); + audience.sendActionBar(component); + }; + }, "actionbar"); + registerAction((args, chance) -> { + List texts = ListUtils.toList(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + String random = texts.get(RandomUtils.generateRandomInt(0, texts.size() - 1)); + random = plugin.getPlaceholderManager().parse(context.holder(), random, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(context.holder()); + audience.sendActionBar(AdventureHelper.miniMessage(random)); + }; + }, "random-actionbar"); + } + + private void registerExpAction() { + registerAction((args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + final Player player = context.holder(); + ExperienceOrb entity = player.getLocation().getWorld().spawn(player.getLocation().clone().add(0,0.5,0), ExperienceOrb.class); + entity.setExperience((int) value.evaluate(context)); + }; + }, "mending"); + registerAction((args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + final Player player = context.holder(); + player.giveExp((int) Math.round(value.evaluate(context))); + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.playSound(audience, Sound.sound(Key.key("minecraft:entity.experience_orb.pickup"), Sound.Source.PLAYER, 1, 1)); + }; + }, "exp"); + registerAction((args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Player player = context.holder(); + player.setLevel((int) Math.max(0, player.getLevel() + value.evaluate(context))); + }; + }, "level"); + } + + private void registerFoodAction() { + registerAction((args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Player player = context.holder(); + player.setFoodLevel((int) (player.getFoodLevel() + value.evaluate(context))); + }; + }, "food"); + registerAction((args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Player player = context.holder(); + player.setSaturation((float) (player.getSaturation() + value.evaluate(context))); + }; + }, "saturation"); + } + + private void registerItemAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + boolean mainOrOff = section.getString("hand", "main").equalsIgnoreCase("main"); + int amount = section.getInt("amount", 1); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Player player = context.holder(); + boolean tempHand = mainOrOff; + EquipmentSlot hand = context.arg(ContextKeys.SLOT); + if (hand == EquipmentSlot.OFF_HAND || hand == EquipmentSlot.HAND) { + tempHand = hand == EquipmentSlot.HAND; + } + ItemStack itemStack = tempHand ? player.getInventory().getItemInMainHand() : player.getInventory().getItemInOffHand(); + itemStack.setAmount(Math.max(0, itemStack.getAmount() + amount)); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at item-amount action which is expected to be `Section`"); + return Action.empty(); + } + }, "item-amount"); + registerAction((args, chance) -> { + int amount; + EquipmentSlot slot; + if (args instanceof Integer integer) { + slot = null; + amount = integer; + } else if (args instanceof Section section) { + slot = Optional.ofNullable(section.getString("slot")) + .map(hand -> EquipmentSlot.valueOf(hand.toUpperCase(Locale.ENGLISH))) + .orElse(null); + amount = section.getInt("amount", 1); + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at durability action which is expected to be `Section`"); + return Action.empty(); + } + return context -> { + if (Math.random() > chance) return; + Player player = context.holder(); + if (player == null) return; + EquipmentSlot tempSlot = slot; + EquipmentSlot equipmentSlot = context.arg(ContextKeys.SLOT); + if (equipmentSlot != null) { + tempSlot = equipmentSlot; + } + if (tempSlot == null) { + return; + } + ItemStack itemStack = player.getInventory().getItem(tempSlot); + if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) + return; + if (itemStack.getItemMeta() == null) + return; + if (amount > 0) { + plugin.getItemManager().decreaseDamage(context.holder(), itemStack, amount); + } else { + plugin.getItemManager().increaseDamage(context.holder(), itemStack, -amount); + } + }; + }, "durability"); + registerAction((args, chance) -> { + if (args instanceof Section section) { + String id = section.getString("item"); + int amount = section.getInt("amount", 1); + boolean toInventory = section.getBoolean("to-inventory", false); + return context -> { + if (Math.random() > chance) return; + Player player = context.holder(); + if (player == null) return; + ItemStack itemStack = plugin.getItemManager().build(context.holder(), id); + if (itemStack != null) { + int maxStack = itemStack.getMaxStackSize(); + int amountToGive = amount; + while (amountToGive > 0) { + int perStackSize = Math.min(maxStack, amountToGive); + amountToGive -= perStackSize; + ItemStack more = itemStack.clone(); + more.setAmount(perStackSize); + if (toInventory) { + PlayerUtils.giveItem(player, more, more.getAmount()); + } else { + PlayerUtils.dropItem(player, more, true, true, false); + } + } + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at give-item action which is expected to be `Section`"); + return Action.empty(); + } + }, "give-item"); + } + + private void registerMoneyAction() { + registerAction((args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + if (!VaultHook.isHooked()) return; + VaultHook.deposit(context.holder(), value.evaluate(context)); + }; + }, "give-money"); + registerAction((args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + if (!VaultHook.isHooked()) return; + VaultHook.withdraw(context.holder(), value.evaluate(context)); + }; + }, "take-money"); + } + + private void registerPotionAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + PotionEffect potionEffect = new PotionEffect( + Objects.requireNonNull(PotionEffectType.getByName(section.getString("type", "BLINDNESS").toUpperCase(Locale.ENGLISH))), + section.getInt("duration", 20), + section.getInt("amplifier", 0) + ); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + context.holder().addPotionEffect(potionEffect); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at potion-effect action which is expected to be `Section`"); + return Action.empty(); + } + }, "potion-effect"); + } + + private void registerSoundAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + Sound sound = Sound.sound( + Key.key(section.getString("key")), + Sound.Source.valueOf(section.getString("source", "PLAYER").toUpperCase(Locale.ENGLISH)), + section.getDouble("volume", 1.0).floatValue(), + section.getDouble("pitch", 1.0).floatValue() + ); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Audience audience = plugin.getSenderFactory().getAudience(context.holder()); + AdventureHelper.playSound(audience, sound); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at sound action which is expected to be `Section`"); + return Action.empty(); + } + }, "sound"); + } + + private void registerPluginExpAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + String pluginName = section.getString("plugin"); + MathValue value = MathValue.auto(section.get("exp")); + String target = section.getString("target"); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Optional.ofNullable(plugin.getIntegrationManager().getLevelerProvider(pluginName)).ifPresentOrElse(it -> { + it.addXp(context.holder(), target, value.evaluate(context)); + }, () -> plugin.getPluginLogger().warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation.")); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plugin-exp action which is expected to be `Section`"); + return Action.empty(); + } + }, "plugin-exp"); + } + + private void registerTitleAction() { + registerAction((args, chance) -> { + if (args instanceof Section section) { + TextValue title = TextValue.auto(section.getString("title", "")); + TextValue subtitle = TextValue.auto(section.getString("subtitle", "")); + int fadeIn = section.getInt("fade-in", 20); + int stay = section.getInt("stay", 30); + int fadeOut = section.getInt("fade-out", 10); + return context -> { + if (Math.random() > chance) return; + final Player player = context.holder(); + if (player == null) return; + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.sendTitle(audience, + AdventureHelper.miniMessage(title.render(context)), + AdventureHelper.miniMessage(subtitle.render(context)), + fadeIn, stay, fadeOut + ); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at title action which is expected to be `Section`"); + return Action.empty(); + } + }, "title"); + registerAction((args, chance) -> { + if (args instanceof Section section) { + List titles = section.getStringList("titles"); + if (titles.isEmpty()) titles.add(""); + List subtitles = section.getStringList("subtitles"); + if (subtitles.isEmpty()) subtitles.add(""); + int fadeIn = section.getInt("fade-in", 20); + int stay = section.getInt("stay", 30); + int fadeOut = section.getInt("fade-out", 10); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + TextValue title = TextValue.auto(titles.get(RandomUtils.generateRandomInt(0, titles.size() - 1))); + TextValue subtitle = TextValue.auto(subtitles.get(RandomUtils.generateRandomInt(0, subtitles.size() - 1))); + final Player player = context.holder(); + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.sendTitle(audience, + AdventureHelper.miniMessage(title.render(context)), + AdventureHelper.miniMessage(subtitle.render(context)), + fadeIn, stay, fadeOut + ); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at random-title action which is expected to be `Section`"); + return Action.empty(); + } + }, "random-title"); + } + + private void registerHologramAction() { + registerAction(((args, chance) -> { + if (args instanceof Section section) { + TextValue text = TextValue.auto(section.getString("text", "")); + MathValue duration = MathValue.auto(section.get("duration", 20)); + boolean position = section.getString("position", "other").equals("other"); + MathValue x = MathValue.auto(section.get("x", 0)); + MathValue y = MathValue.auto(section.get("y", 0)); + MathValue z = MathValue.auto(section.get("z", 0)); + boolean applyCorrection = section.getBoolean("apply-correction", false); + boolean onlyShowToOne = !section.getBoolean("visible-to-all", false); + int range = section.getInt("range", 32); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Player owner = context.holder(); + Location location = position ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : owner.getLocation().clone(); + location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + Pos3 pos3 = Pos3.from(location); + if (applyCorrection) { + Optional optionalState = optionalWorld.get().getBlockState(pos3); + if (optionalState.isPresent()) { + if (optionalState.get().type() instanceof CropBlock cropBlock) { + CropConfig config = cropBlock.config(optionalState.get()); + int point = cropBlock.point(optionalState.get()); + if (config != null) { + int tempPoints = point; + while (tempPoints >= 0) { + Map.Entry entry = config.getFloorStageEntry(tempPoints); + CropStageConfig stage = entry.getValue(); + if (stage.stageID() != null) { + location.add(0, stage.displayInfoOffset(), 0); + break; + } + tempPoints = stage.point() - 1; + } + } + } + } + } + ArrayList viewers = new ArrayList<>(); + if (onlyShowToOne) { + if (owner == null) return; + viewers.add(owner); + } else { + for (Player player : owner.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= range) { + viewers.add(player); + } + } + } + Component component = AdventureHelper.miniMessage(text.render(context)); + for (Player viewer : viewers) { + HologramManager.getInstance().showHologram(viewer, location, component, (int) (duration.evaluate(context) * 50)); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at hologram action which is expected to be `Section`"); + return Action.empty(); + } + }), "hologram"); + } + + private void registerSwingHandAction() { + registerAction((args, chance) -> { + boolean arg = (boolean) args; + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + SparrowHeart.getInstance().swingHand(context.holder(), arg ? HandSlot.MAIN : HandSlot.OFF); + }; + }, "swing-hand"); + } + + private void registerForceTickAction() { + registerAction((args, chance) -> context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Pos3 pos3 = Pos3.from(location); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> { + state.type().randomTick(state, world, pos3); + state.type().scheduledTick(state, world, pos3); + })); + }, "force-tick"); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandFeature.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandFeature.java new file mode 100644 index 000000000..768e02174 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandFeature.java @@ -0,0 +1,61 @@ +/* + * 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.bukkit.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.common.command.AbstractCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.sender.SenderFactory; +import net.momirealms.customcrops.common.util.Pair; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.incendo.cloud.bukkit.data.Selector; + +import java.util.Collection; + +public abstract class BukkitCommandFeature extends AbstractCommandFeature { + + public BukkitCommandFeature(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + @SuppressWarnings("unchecked") + protected SenderFactory getSenderFactory() { + return (SenderFactory) BukkitCustomCropsPlugin.getInstance().getSenderFactory(); + } + + public Pair resolveSelector(Selector selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) { + Collection entities = selector.values(); + if (entities.size() == 1) { + return Pair.of(single, Component.text(entities.iterator().next().getName())); + } else { + return Pair.of(multiple, Component.text(entities.size())); + } + } + + public Pair resolveSelector(Collection selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) { + if (selector.size() == 1) { + return Pair.of(single, Component.text(selector.iterator().next().getName())); + } else { + return Pair.of(multiple, Component.text(selector.size())); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java new file mode 100644 index 000000000..beed78ba1 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java @@ -0,0 +1,70 @@ +/* + * 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.bukkit.command; + +import net.kyori.adventure.util.Index; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.bukkit.command.feature.DebugDataCommand; +import net.momirealms.customcrops.bukkit.command.feature.ReloadCommand; +import net.momirealms.customcrops.common.command.AbstractCommandManager; +import net.momirealms.customcrops.common.command.CommandFeature; +import net.momirealms.customcrops.common.sender.Sender; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.SenderMapper; +import org.incendo.cloud.bukkit.CloudBukkitCapabilities; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.paper.LegacyPaperCommandManager; +import org.incendo.cloud.setting.ManagerSetting; + +import java.util.List; + +public class BukkitCommandManager extends AbstractCommandManager { + + private final List> FEATURES = List.of( + new ReloadCommand(this), + new DebugDataCommand(this) + ); + + private final Index> INDEX = Index.create(CommandFeature::getFeatureID, FEATURES); + + public BukkitCommandManager(BukkitCustomCropsPlugin plugin) { + super(plugin, new LegacyPaperCommandManager<>( + plugin.getBoostrap(), + ExecutionCoordinator.simpleCoordinator(), + SenderMapper.identity() + )); + final LegacyPaperCommandManager manager = (LegacyPaperCommandManager) getCommandManager(); + manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true); + if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) { + manager.registerBrigadier(); + manager.brigadierManager().setNativeNumberSuggestions(true); + } else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) { + manager.registerAsynchronousCompletions(); + } + } + + @Override + protected Sender wrapSender(CommandSender sender) { + return ((BukkitCustomCropsPlugin) plugin).getSenderFactory().wrap(sender); + } + + @Override + public Index> getFeatures() { + return INDEX; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugDataCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugDataCommand.java new file mode 100644 index 000000000..2de532dfc --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugDataCommand.java @@ -0,0 +1,73 @@ +/* + * 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.bukkit.command.feature; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.world.Pos3; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +import java.util.Optional; + +public class DebugDataCommand extends BukkitCommandFeature { + + public DebugDataCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .flag(manager.flagBuilder("this").build()) + .handler(context -> { + Player player = context.sender(); + Location location; + if (context.flags().hasFlag("this")) { + location = player.getLocation(); + } else { + Block block = player.getTargetBlockExact(10); + if (block == null) return; + location = block.getLocation(); + } + BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(location.getWorld()).ifPresent(world -> { + Optional state = world.getBlockState(Pos3.from(location)); + if (state.isPresent()) { + BukkitCustomCropsPlugin.getInstance().getSenderFactory().wrap(player) + .sendMessage(AdventureHelper.miniMessage(state.get().toString())); + } else { + BukkitCustomCropsPlugin.getInstance().getSenderFactory().wrap(player) + .sendMessage(AdventureHelper.miniMessage("Data not found")); + } + }); + }); + } + + @Override + public String getFeatureID() { + return "debug_data"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ReloadCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ReloadCommand.java new file mode 100644 index 000000000..44c0ef466 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ReloadCommand.java @@ -0,0 +1,50 @@ +/* + * 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.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class ReloadCommand extends BukkitCommandFeature { + + public ReloadCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s")) + .handler(context -> { + long time1 = System.currentTimeMillis(); + BukkitCustomCropsPlugin.getInstance().reload(); + handleFeedback(context, MessageConstants.COMMAND_RELOAD_SUCCESS, Component.text(System.currentTimeMillis() - time1)); + }); + } + + @Override + public String getFeatureID() { + return "reload"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java new file mode 100644 index 000000000..81ab091b9 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -0,0 +1,249 @@ +package net.momirealms.customcrops.bukkit.config; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; +import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.common.ScalarStyle; +import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.exceptions.ConstructorException; +import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.nodes.Tag; +import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; +import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; +import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; +import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; +import dev.dejvokep.boostedyaml.utils.format.NodeRole; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.block.CropStageConfig; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.util.PluginUtils; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.helper.VersionHelper; +import net.momirealms.customcrops.common.locale.TranslationManager; +import net.momirealms.customcrops.common.plugin.CustomCropsProperties; +import net.momirealms.customcrops.common.util.ListUtils; +import org.bukkit.Bukkit; +import org.bukkit.Particle; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; + +public class BukkitConfigManager extends ConfigManager { + + private static YamlDocument MAIN_CONFIG; + + public static YamlDocument getMainConfig() { + return MAIN_CONFIG; + } + + public BukkitConfigManager(BukkitCustomCropsPlugin plugin) { + super(plugin); + } + + @Override + public void load() { + String configVersion = CustomCropsProperties.getValue("config"); + try (InputStream inputStream = new FileInputStream(resolveConfig("config.yml").toFile())) { + MAIN_CONFIG = YamlDocument.create( + inputStream, + plugin.getResourceStream("config.yml"), + GeneralSettings.builder() + .setRouteSeparator('.') + .setUseDefaults(false) + .build(), + LoaderSettings + .builder() + .setAutoUpdate(true) + .build(), + DumperSettings.builder() + .setScalarFormatter((tag, value, role, def) -> { + if (role == NodeRole.KEY) { + return ScalarStyle.PLAIN; + } else { + return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN; + } + }) + .build(), + UpdaterSettings + .builder() + .setVersioning(new BasicVersioning("config-version")) + .addIgnoredRoute(configVersion, "other-settings.placeholder-register", '.') + .build() + ); + MAIN_CONFIG.save(resolveConfig("config.yml").toFile()); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.loadSettings(); + this.loadConfigs(); + } + + private void loadSettings() { + YamlDocument config = getMainConfig(); + + TranslationManager.forceLocale(TranslationManager.parseLocale(config.getString("force-locale", ""))); + AdventureHelper.legacySupport = config.getBoolean("other-settings.legacy-color-code-support", true); + + metrics = config.getBoolean("metrics", true); + checkUpdate = config.getBoolean("update-checker", true); + debug = config.getBoolean("debug", false); + + protectOriginalLore = config.getBoolean("other-settings.protect-original-lore", false); + doubleCheck = config.getBoolean("other-settings.double-check", false); + + enableScarecrow = config.getBoolean("mechanics.scarecrow.enable", true); + scarecrow = new HashSet<>(ListUtils.toList(config.get("mechanics.scarecrow.id"))); + scarecrowExistenceForm = CustomForm.valueOf(config.getString("mechanics.scarecrow.type", "ITEM_FRAME")).existenceForm(); + scarecrowRange = config.getInt("mechanics.scarecrow.range", 7); + scarecrowProtectChunk = config.getBoolean("mechanics.scarecrow.protect-chunk", false); + + enableGreenhouse = config.getBoolean("mechanics.greenhouse.enable", true); + greenhouse = new HashSet<>(ListUtils.toList(config.get("mechanics.greenhouse.id"))); + greenhouseExistenceForm = CustomForm.valueOf(config.getString("mechanics.greenhouse.type", "CHORUS")).existenceForm(); + greenhouseRange = config.getInt("mechanics.greenhouse.range", 5); + + syncSeasons = config.getBoolean("mechanics.sync-season.enable", false); + referenceWorld = config.getString("mechanics.sync-season.reference", "world"); + + itemDetectOrder = config.getStringList("other-settings.item-detection-order").toArray(new String[0]); + + absoluteWorldPath = config.getString("worlds.absolute-world-folder-path"); + + defaultQualityRatio = getQualityRatio(config.getString("mechanics.default-quality-ratio", "17/2/1")); + + hasNamespace = PluginUtils.isEnabled("ItemsAdder"); + } + + @Override + public void saveResource(String filePath) { + File file = new File(plugin.getDataFolder(), filePath); + if (!file.exists()) { + plugin.getBoostrap().saveResource(filePath, false); + addDefaultNamespace(file); + } + } + + @Override + public void unload() { + this.clearConfigs(); + } + + private void loadConfigs() { + Deque fileDeque = new ArrayDeque<>(); + for (ConfigType type : ConfigType.values()) { + File typeFolder = new File(plugin.getDataFolder(), "contents" + File.separator + type.path()); + if (!typeFolder.exists()) { + if (!typeFolder.mkdirs()) return; + saveResource("contents" + File.separator + type.path() + File.separator + "default.yml"); + } + fileDeque.push(typeFolder); + while (!fileDeque.isEmpty()) { + File file = fileDeque.pop(); + File[] files = file.listFiles(); + if (files == null) continue; + for (File subFile : files) { + if (subFile.isDirectory()) { + fileDeque.push(subFile); + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { + try { + YamlDocument document = plugin.getConfigManager().loadData(subFile); + boolean save = false; + for (Map.Entry entry : document.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section section) { + if (type.parse(this, entry.getKey(), section)) { + save = true; + } + } + } + if (save) { + document.save(subFile); + } + } catch (ConstructorException e) { + plugin.getPluginLogger().warn("Could not load config file: " + subFile.getAbsolutePath() + ". Is it a corrupted file?"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } + } + } + + private void clearConfigs() { + Registries.CROP.clear(); + Registries.SEED_TO_CROP.clear(); + Registries.STAGE_TO_CROP_UNSAFE.clear(); + + Registries.SPRINKLER.clear(); + Registries.ITEM_TO_SPRINKLER.clear(); + + Registries.POT.clear(); + Registries.ITEM_TO_POT.clear(); + + Registries.FERTILIZER.clear(); + Registries.ITEM_TO_SPRINKLER.clear(); + + Registries.WATERING_CAN.clear(); + + Registries.ITEMS.clear(); + Registries.BLOCKS.clear(); + } + + @Override + public void registerWateringCanConfig(WateringCanConfig config) { + Registries.WATERING_CAN.register(config.id(), config); + Registries.ITEMS.register(config.itemID(), BuiltInItemMechanics.WATERING_CAN.mechanic()); + } + + @Override + public void registerFertilizerConfig(FertilizerConfig config) { + Registries.FERTILIZER.register(config.id(), config); + Registries.ITEM_TO_FERTILIZER.register(config.itemID(), config); + Registries.ITEMS.register(config.itemID(), BuiltInItemMechanics.FERTILIZER.mechanic()); + } + + @Override + public void registerCropConfig(CropConfig config) { + Registries.CROP.register(config.id(), config); + Registries.SEED_TO_CROP.register(config.seed(), config); + Registries.ITEMS.register(config.seed(), BuiltInItemMechanics.SEED.mechanic()); + for (CropStageConfig stageConfig : config.stages()) { + String stageID = stageConfig.stageID(); + if (stageID != null) { + List list = Registries.STAGE_TO_CROP_UNSAFE.get(stageID); + if (list != null) { + list.add(config); + } else { + Registries.STAGE_TO_CROP_UNSAFE.register(stageID, new ArrayList<>(List.of(config))); + Registries.BLOCKS.register(stageID, BuiltInBlockMechanics.CROP.mechanic()); + } + } + } + } + + @Override + public void registerPotConfig(PotConfig config) { + Registries.POT.register(config.id(), config); + for (String pot : config.blocks()) { + Registries.ITEM_TO_POT.register(pot, config); + Registries.BLOCKS.register(pot, BuiltInBlockMechanics.POT.mechanic()); + } + } + + @Override + public void registerSprinklerConfig(SprinklerConfig config) { + Registries.SPRINKLER.register(config.id(), config); + for (String id : new HashSet<>(List.of(config.threeDItem(), config.threeDItemWithWater()))) { + Registries.ITEM_TO_SPRINKLER.register(id, config); + Registries.BLOCKS.register(id, BuiltInBlockMechanics.SPRINKLER.mechanic()); + } + if (config.twoDItem() != null) { + Registries.ITEM_TO_SPRINKLER.register(config.twoDItem(), config); + Registries.ITEMS.register(config.twoDItem(), BuiltInItemMechanics.SPRINKLER_ITEM.mechanic()); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java new file mode 100644 index 000000000..092204074 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -0,0 +1,320 @@ +/* + * 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.bukkit.config; + +import com.google.common.base.Preconditions; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.CustomForm; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.Registries; +import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.block.CropStageConfig; +import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.requirement.RequirementManager; +import net.momirealms.customcrops.common.util.Pair; +import net.momirealms.customcrops.common.util.TriFunction; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; + +/** + * Configuration types for various mechanics. + */ +public class ConfigType { + + public static final ConfigType WATERING_CAN = of( + "watering-cans", + (manager, id, section) -> { + + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + + WateringCanConfig config = WateringCanConfig.builder() + .id(id) + .itemID(section.getString("item")) + .storage(section.getInt("capacity", 3)) + .wateringAmount(section.getInt("water", 1)) + .infinite(section.getBoolean("infinite", false)) + .width(section.getInt("effective-range.width", 1)) + .length(section.getInt("effective-range.length", 1)) + .potWhitelist(new HashSet<>(section.getStringList("pot-whitelist"))) + .sprinklerWhitelist(new HashSet<>(section.getStringList("sprinkler-whitelist"))) + .dynamicLore(section.getBoolean("dynamic-lore")) + .lore(section.getStringList("dynamic-lore.lore").stream().map(TextValue::auto).toList()) + .fillMethods(manager.getFillMethods(section.getSection("fill-method"))) + .requirements(BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class).parseRequirements(section.getSection("requirements"), true)) + .fullActions(pam.parseActions(section.getSection("events.full"))) + .addWaterActions(pam.parseActions(section.getSection("events.add_water"))) + .consumeWaterActions(pam.parseActions(section.getSection("events.consume_water"))) + .runOutOfWaterActions(pam.parseActions(section.getSection("events.no_water"))) + .wrongPotActions(pam.parseActions(section.getSection("events.wrong_pot"))) + .wrongSprinklerActions(pam.parseActions(section.getSection("events.wrong_sprinkler"))) + .appearances(manager.getInt2IntMap(section.getSection("appearance"))) + .waterBar(section.contains("water-bar") ? WaterBar.of( + section.getString("water-bar.left", ""), + section.getString("water-bar.empty", ""), + section.getString("water-bar.full", ""), + section.getString("water-bar.right", "") + ) : null) + .build(); + + manager.registerWateringCanConfig(config); + return false; + } + ); + + public static final ConfigType FERTILIZER = of( + "fertilizers", + (manager, id, section) -> { + String typeName = Preconditions.checkNotNull(section.getString("type"), "Fertilizer type can't be null").toLowerCase(Locale.ENGLISH); + FertilizerType type = Registries.FERTILIZER_TYPE.get(typeName); + if (type == null) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Fertilizer type " + typeName + " not found"); + return false; + } + FertilizerConfig config = type.parse(manager, id, section); + manager.registerFertilizerConfig(config); + return false; + } + ); + + public static final ConfigType POT = of( + "pots", + (manager, id, section) -> { + + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + ActionManager bam = BukkitCustomCropsPlugin.getInstance().getActionManager(CustomCropsBlockState.class); + RequirementManager prm = BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class); + + PotConfig config = PotConfig.builder() + .id(id) + .isRainDropAccepted(section.getBoolean("absorb-rainwater", false)) + .isNearbyWaterAccepted(section.getBoolean("absorb-nearby-water", false)) + .maxFertilizers(section.getInt("max-fertilizers", 1)) + .basicAppearance(Pair.of(section.getString("base.dry"), section.getString("base.wet"))) + .potAppearanceMap(manager.getFertilizedPotMap(section.getSection("fertilized-pots"))) + .wateringMethods(manager.getWateringMethods(section.getSection("fill-method"))) + .addWaterActions(pam.parseActions(section.getSection("events.add_water"))) + .placeActions(pam.parseActions(section.getSection("events.place"))) + .breakActions(pam.parseActions(section.getSection("events.break"))) + .interactActions(pam.parseActions(section.getSection("events.interact"))) + .reachLimitActions(pam.parseActions(section.getSection("events.reach_limit"))) + .fullWaterActions(pam.parseActions(section.getSection("events.full"))) + .tickActions(bam.parseActions(section.getSection("events.tick"))) + .useRequirements(prm.parseRequirements(section.getSection("requirements.use"), true)) + .placeRequirements(prm.parseRequirements(section.getSection("requirements.place"), true)) + .breakRequirements(prm.parseRequirements(section.getSection("requirements.break"), true)) + .waterBar(section.contains("water-bar") ? WaterBar.of( + section.getString("water-bar.left", ""), + section.getString("water-bar.empty", ""), + section.getString("water-bar.full", ""), + section.getString("water-bar.right", "") + ) : null) + .build(); + + manager.registerPotConfig(config); + return false; + } + ); + + public static final ConfigType CROP = of( + "crops", + (manager, id, section) -> { + + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + ActionManager bam = BukkitCustomCropsPlugin.getInstance().getActionManager(CustomCropsBlockState.class); + RequirementManager prm = BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class); + + boolean needUpdate = false; + + ExistenceForm form = CustomForm.valueOf(section.getString("type").toUpperCase(Locale.ENGLISH)).existenceForm(); + + Section growConditionSection = section.getSection("grow-conditions"); + if (growConditionSection != null) { + for (Map.Entry entry : growConditionSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + if (inner.contains("type")) { + needUpdate = true; + break; + } + } + } + } + + if (needUpdate) { + section.remove("grow-conditions"); + section.set("grow-conditions.default.point", 1); + Section newSection = section.createSection("grow-conditions.default.conditions"); + newSection.setValue(growConditionSection.getStoredValue()); + } + + Section pointSection = section.getSection("points"); + if (pointSection == null) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("points section not found in crop[" + id + "]"); + return false; + } + + ArrayList builders = new ArrayList<>(); + for (Map.Entry entry : pointSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + int point = Integer.parseInt(entry.getKey()); + CropStageConfig.Builder builder = CropStageConfig.builder() + .point(point) + .displayInfoOffset(inner.getDouble("hologram-offset-correction")) + .stageID(inner.getString("model")) + .breakRequirements(prm.parseRequirements(inner.getSection("requirements.break"), true)) + .interactRequirements(prm.parseRequirements(inner.getSection("requirements.interact"), true)) + .breakActions(pam.parseActions(inner.getSection("events.break"))) + .interactActions(pam.parseActions(inner.getSection("events.interact"))) + .growActions(bam.parseActions(inner.getSection("events.grow"))); + builders.add(builder); + } + } + + CropConfig config = CropConfig.builder() + .id(id) + .seed(section.getString("seed")) + .rotation(section.getBoolean("random-rotation", false)) + .maxPoints(section.getInt("max-points", 1)) + .potWhitelist(new HashSet<>(section.getStringList("pot-whitelist"))) + .wrongPotActions(pam.parseActions(section.getSection("events.wrong_pot"))) + .plantActions(pam.parseActions(section.getSection("events.plant"))) + .breakActions(pam.parseActions(section.getSection("events.break"))) + .interactActions(pam.parseActions(section.getSection("events.interact"))) + .reachLimitActions(pam.parseActions(section.getSection("events.reach_limit"))) + .interactRequirements(prm.parseRequirements(section.getSection("requirements.interact"), true)) + .plantRequirements(prm.parseRequirements(section.getSection("requirements.plant"), true)) + .breakRequirements(prm.parseRequirements(section.getSection("requirements.break"), true)) + .boneMeals(manager.getBoneMeals(section.getSection("custom-bone-meal"))) + .deathConditions(manager.getDeathConditions(section.getSection("death-conditions"), form)) + .growConditions(manager.getGrowConditions(section.getSection("grow-conditions"))) + .stages(builders) + .build(); + + manager.registerCropConfig(config); + return needUpdate; + } + ); + + public static final ConfigType SPRINKLER = of( + "sprinklers", + (manager, id, section) -> { + int rangeValue = section.getInt("range",1); + int workingMode = section.getInt("working-mode", 1); + int[][] range; + if (workingMode == 1) { + int blocks = 4 * rangeValue * rangeValue + 4 * rangeValue + 1; + range = new int[blocks][2]; + int index = 0; + for (int i = -rangeValue; i <= rangeValue; i++) { + for (int j = -rangeValue; j <= rangeValue; j++) { + range[index++] = new int[]{i, j}; + } + } + } else if (workingMode == 2) { + int blocks = (2 * rangeValue * rangeValue) + 2 * rangeValue + 1; + range = new int[blocks][2]; + int index = 0; + for (int i = -rangeValue; i <= rangeValue; i++) { + for (int j = -rangeValue; j <= rangeValue; j++) { + if (Math.abs(i) + Math.abs(j) <= rangeValue) { + range[index++] = new int[]{i, j}; + } + } + } + } else { + throw new IllegalArgumentException("Unrecognized working mode: " + workingMode); + } + + ActionManager pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class); + ActionManager bam = BukkitCustomCropsPlugin.getInstance().getActionManager(CustomCropsBlockState.class); + RequirementManager prm = BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class); + + SprinklerConfig config = SprinklerConfig.builder() + .id(id) + .range(range) + .storage(section.getInt("storage", 4)) + .infinite(section.getBoolean("infinite", false)) + .twoDItem(section.getString("2D-item")) + .sprinklingAmount(section.getInt("water", 1)) + .threeDItem(section.getString("3D-item")) + .threeDItemWithWater(section.getString("3D-item-with-water")) + .wateringMethods(manager.getWateringMethods(section.getSection("fill-method"))) + .potWhitelist(new HashSet<>(section.getStringList("pot-whitelist"))) + .existenceForm(CustomForm.valueOf(section.getString("type", "ITEM_FRAME").toUpperCase(Locale.ENGLISH)).existenceForm()) + .addWaterActions(pam.parseActions(section.getSection("events.add_water"))) + .breakActions(pam.parseActions(section.getSection("events.break"))) + .placeActions(pam.parseActions(section.getSection("events.place"))) + .fullWaterActions(pam.parseActions(section.getSection("events.full"))) + .reachLimitActions(pam.parseActions(section.getSection("events.reach_limit"))) + .interactActions(pam.parseActions(section.getSection("events.interact"))) + .workActions(bam.parseActions(section.getSection("events.work"))) + .useRequirements(prm.parseRequirements(section.getSection("requirements.use"), true)) + .placeRequirements(prm.parseRequirements(section.getSection("requirements.place"), true)) + .breakRequirements(prm.parseRequirements(section.getSection("requirements.break"), true)) + .waterBar(section.contains("water-bar") ? WaterBar.of( + section.getString("water-bar.left", ""), + section.getString("water-bar.empty", ""), + section.getString("water-bar.full", ""), + section.getString("water-bar.right", "") + ) : null) + .build(); + + manager.registerSprinklerConfig(config); + return false; + } + ); + + private static final ConfigType[] values = new ConfigType[] {CROP, SPRINKLER, WATERING_CAN, POT, FERTILIZER}; + + public static ConfigType[] values() { + return values; + } + + private final String path; + private final TriFunction argumentConsumer; + + public ConfigType(String path, TriFunction argumentConsumer) { + this.path = path; + this.argumentConsumer = argumentConsumer; + } + + public static ConfigType of(String path, TriFunction argumentConsumer) { + return new ConfigType(path, argumentConsumer); + } + + public String path() { + return path; + } + + public boolean parse(ConfigManager manager, String id, Section section) { + return argumentConsumer.apply(manager, id, section); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/BukkitIntegrationManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/BukkitIntegrationManager.java new file mode 100644 index 000000000..f1b37b59c --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/BukkitIntegrationManager.java @@ -0,0 +1,164 @@ +/* + * 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.bukkit.integration; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.integration.IntegrationManager; +import net.momirealms.customcrops.api.integration.ItemProvider; +import net.momirealms.customcrops.api.integration.LevelerProvider; +import net.momirealms.customcrops.api.integration.SeasonProvider; +import net.momirealms.customcrops.bukkit.integration.item.*; +import net.momirealms.customcrops.bukkit.integration.level.*; +import net.momirealms.customcrops.bukkit.integration.papi.CustomCropsPapi; +import net.momirealms.customcrops.bukkit.integration.season.AdvancedSeasonsProvider; +import net.momirealms.customcrops.bukkit.integration.season.RealisticSeasonsProvider; +import net.momirealms.customcrops.bukkit.item.BukkitItemManager; +import net.momirealms.customcrops.bukkit.world.BukkitWorldManager; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; + +public class BukkitIntegrationManager implements IntegrationManager { + + private final BukkitCustomCropsPlugin plugin; + private final HashMap levelerProviders = new HashMap<>(); + + public BukkitIntegrationManager(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + try { + this.load(); + } catch (Exception e) { + plugin.getPluginLogger().warn("Failed to load integrations", e); + } + } + + @Override + public void disable() { + this.levelerProviders.clear(); + } + + @Override + public void load() { + if (isHooked("MMOItems")) { + registerItemProvider(new MMOItemsItemProvider()); + } + if (isHooked("Zaphkiel")) { + registerItemProvider(new ZaphkielItemProvider()); + } + if (isHooked("NeigeItems")) { + registerItemProvider(new NeigeItemsItemProvider()); + } + if (isHooked("CustomFishing", "2.2", "2.3", "2.4")) { + registerItemProvider(new CustomFishingItemProvider()); + } + if (isHooked("MythicMobs", "5")) { + registerItemProvider(new MythicMobsItemProvider()); + } + if (isHooked("EcoJobs")) { + registerLevelerProvider(new EcoJobsLevelerProvider()); + } + if (isHooked("EcoSkills")) { + registerLevelerProvider(new EcoSkillsLevelerProvider()); + } + if (isHooked("Jobs")) { + registerLevelerProvider(new JobsRebornLevelerProvider()); + } + if (isHooked("MMOCore")) { + registerLevelerProvider(new MMOCoreLevelerProvider()); + } + if (isHooked("mcMMO")) { + registerLevelerProvider(new McMMOLevelerProvider()); + } + if (isHooked("AureliumSkills")) { + registerLevelerProvider(new AureliumSkillsProvider()); + } + if (isHooked("AuraSkills")) { + registerLevelerProvider(new AuraSkillsLevelerProvider()); + } + if (isHooked("RealisticSeasons")) { + registerSeasonProvider(new RealisticSeasonsProvider()); + } else if (isHooked("AdvancedSeasons", "1.4", "1.5", "1.6")) { + registerSeasonProvider(new AdvancedSeasonsProvider()); + } + if (isHooked("Vault")) { + VaultHook.init(); + } + if (isHooked("PlaceholderAPI")) { + new CustomCropsPapi(plugin).load(); + } + } + + private boolean isHooked(String hooked) { + if (Bukkit.getPluginManager().getPlugin(hooked) != null) { + plugin.getPluginLogger().info(hooked + " hooked!"); + return true; + } + return false; + } + + @SuppressWarnings("deprecation") + private boolean isHooked(String hooked, String... versionPrefix) { + Plugin p = Bukkit.getPluginManager().getPlugin(hooked); + if (p != null) { + String ver = p.getDescription().getVersion(); + for (String prefix : versionPrefix) { + if (ver.startsWith(prefix)) { + plugin.getPluginLogger().info(hooked + " hooked!"); + return true; + } + } + } + return false; + } + + @Override + public boolean registerLevelerProvider(@NotNull LevelerProvider leveler) { + if (levelerProviders.containsKey(leveler.identifier())) return false; + levelerProviders.put(leveler.identifier(), leveler); + return true; + } + + @Override + public boolean unregisterLevelerProvider(@NotNull String id) { + return levelerProviders.remove(id) != null; + } + + @Override + @Nullable + public LevelerProvider getLevelerProvider(String plugin) { + return levelerProviders.get(plugin); + } + + @Override + public void registerSeasonProvider(@NotNull SeasonProvider season) { + ((BukkitWorldManager) plugin.getWorldManager()).seasonProvider(season); + } + + @Override + public boolean registerItemProvider(@NotNull ItemProvider item) { + return ((BukkitItemManager) plugin.getItemManager()).registerItemProvider(item); + } + + @Override + public boolean unregisterItemProvider(@NotNull String id) { + return ((BukkitItemManager) plugin.getItemManager()).unregisterItemProvider(id); + } +} 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 new file mode 100644 index 000000000..c40d81522 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java @@ -0,0 +1,424 @@ +package net.momirealms.customcrops.bukkit.integration.adaptor; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.CompoundTag; +import com.flowpowered.nbt.stream.NBTInputStream; +import com.flowpowered.nbt.stream.NBTOutputStream; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.Registries; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.world.*; +import net.momirealms.customcrops.api.core.world.adaptor.AbstractWorldAdaptor; +import net.momirealms.customcrops.api.util.StringUtils; +import net.momirealms.customcrops.common.helper.GsonHelper; +import net.momirealms.customcrops.common.helper.VersionHelper; +import net.momirealms.customcrops.common.util.Key; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class BukkitWorldAdaptor extends AbstractWorldAdaptor { + + private static BiFunction regionFileProvider; + private static Function worldFolderProvider; + private static final NamespacedKey WORLD_DATA = new NamespacedKey(BukkitCustomCropsPlugin.getInstance().getBoostrap(), "data"); + private static final String DATA_FILE = "customcrops.dat"; + + public BukkitWorldAdaptor() { + worldFolderProvider = (world -> { + if (ConfigManager.absoluteWorldPath().isEmpty()) { + return world.getWorldFolder(); + } else { + return new File(ConfigManager.absoluteWorldPath(), world.getName()); + } + }); + regionFileProvider = (world, pos) -> new File(worldFolderProvider.apply(world), "customcrops" + File.separator + getRegionDataFile(pos)); + + } + + public static void regionFileProvider(BiFunction regionFileProvider) { + BukkitWorldAdaptor.regionFileProvider = regionFileProvider; + } + + public static void worldFolderProvider(Function worldFolderProvider) { + BukkitWorldAdaptor.worldFolderProvider = worldFolderProvider; + } + + @Override + public World getWorld(String worldName) { + return Bukkit.getWorld(worldName); + } + + @Override + public CustomCropsWorld adapt(Object world) { + return CustomCropsWorld.create((World) world, this); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public WorldExtraData loadExtraData(World world) { + if (VersionHelper.isVersionNewerThan1_18()) { + // init world basic info + String json = world.getPersistentDataContainer().get(WORLD_DATA, PersistentDataType.STRING); + WorldExtraData data = (json == null || json.equals("null")) ? WorldExtraData.empty() : GsonHelper.get().fromJson(json, WorldExtraData.class); + if (data == null) data = WorldExtraData.empty(); + return data; + } else { + File data = new File(getWorldFolder(world), DATA_FILE); + if (data.exists()) { + byte[] fileBytes = new byte[(int) data.length()]; + try (FileInputStream fis = new FileInputStream(data)) { + fis.read(fileBytes); + } catch (IOException e) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.getName() + "] Failed to load extra data from " + data.getAbsolutePath(), e); + } + String jsonContent = new String(fileBytes, StandardCharsets.UTF_8); + return GsonHelper.get().fromJson(jsonContent, WorldExtraData.class); + } else { + return WorldExtraData.empty(); + } + } + } + + @Override + public void saveExtraData(CustomCropsWorld world) { + if (VersionHelper.isVersionNewerThan1_18()) { + world.world().getPersistentDataContainer().set(WORLD_DATA, PersistentDataType.STRING, + GsonHelper.get().toJson(world.extraData())); + } else { + File data = new File(getWorldFolder(world.world()), DATA_FILE); + try (FileWriter file = new FileWriter(data)) { + GsonHelper.get().toJson(world.extraData(), file); + } catch (IOException e) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Failed to save extra data to " + data.getAbsolutePath(), e); + } + } + } + + @Nullable + @Override + public CustomCropsRegion loadRegion(CustomCropsWorld world, RegionPos pos, boolean createIfNotExist) { + File data = getRegionDataFile(world.world(), pos); + // if the data file not exists + if (!data.exists()) { + return createIfNotExist ? world.createRegion(pos) : null; + } else { + // load region from local files + try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(data))) { + DataInputStream dataStream = new DataInputStream(bis); + CustomCropsRegion region = deserializeRegion(world, dataStream, pos); + dataStream.close(); + return region; + } catch (Exception e) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Failed to load CustomCrops region data at " + pos + ". Deleting the corrupted region.", e); + boolean success = data.delete(); + if (success) { + return createIfNotExist ? world.createRegion(pos) : null; + } else { + throw new RuntimeException("[" + world.worldName() + "] Failed to delete corrupted CustomCrops region data at " + pos); + } + } + } + } + + @Nullable + @Override + public CustomCropsChunk loadChunk(CustomCropsWorld world, ChunkPos pos, boolean createIfNotExist) { + CustomCropsRegion region = world.getOrCreateRegion(pos.toRegionPos()); + // In order to reduce frequent disk reads to determine whether a region exists, we read the region into the cache + if (!region.isLoaded()) { + region.load(); + } + byte[] bytes = region.getCachedChunkBytes(pos); + if (bytes == null) { + return createIfNotExist ? world.createChunk(pos) : null; + } else { + try { + long time1 = System.currentTimeMillis(); + DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(bytes)); + CustomCropsChunk chunk = deserializeChunk(world, dataStream); + dataStream.close(); + long time2 = System.currentTimeMillis(); + BukkitCustomCropsPlugin.getInstance().debug("[" + world.worldName() + "] Took " + (time2-time1) + "ms to load chunk " + pos + " from cached region"); + return chunk; + } catch (IOException e) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Failed to load CustomCrops data at " + pos, e); + region.removeCachedChunk(pos); + return createIfNotExist ? world.createChunk(pos) : null; + } + } + } + + @Override + public void saveRegion(CustomCropsWorld world, CustomCropsRegion region) { + File file = getRegionDataFile(world.world(), region.regionPos()); + long time1 = System.currentTimeMillis(); + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) { + bos.write(serializeRegion(region)); + long time2 = System.currentTimeMillis(); + BukkitCustomCropsPlugin.getInstance().debug("[" + world.worldName() + "] Took " + (time2-time1) + "ms to save region " + region.regionPos()); + } catch (IOException e) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Failed to save CustomCrops region data." + region.regionPos(), e); + } + } + + @Override + public void saveChunk(CustomCropsWorld world, CustomCropsChunk chunk) { + RegionPos pos = chunk.chunkPos().toRegionPos(); + Optional region = world.getLoadedRegion(pos); + if (region.isEmpty()) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Region " + pos + " unloaded before chunk " + chunk.chunkPos() + " saving."); + } else { + CustomCropsRegion cropsRegion = region.get(); + SerializableChunk serializableChunk = toSerializableChunk(chunk); + if (serializableChunk.canPrune()) { + cropsRegion.removeCachedChunk(chunk.chunkPos()); + } else { + cropsRegion.setCachedChunk(chunk.chunkPos(), serializeChunk(serializableChunk)); + } + } + } + + @Override + public String getName(World world) { + return world.getName(); + } + + @Override + public long getWorldFullTime(World world) { + return world.getFullTime(); + } + + @Override + public int priority() { + return BUKKIT_WORLD_PRIORITY; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private CustomCropsRegion deserializeRegion(CustomCropsWorld world, DataInputStream dataStream, RegionPos pos) throws IOException { + int regionVersion = dataStream.readByte(); + int regionX = dataStream.readInt(); + int regionZ = dataStream.readInt(); + RegionPos regionPos = RegionPos.of(regionX, regionZ); + ConcurrentHashMap map = new ConcurrentHashMap<>(); + int chunkAmount = dataStream.readInt(); + for (int i = 0; i < chunkAmount; i++) { + int chunkX = dataStream.readInt(); + int chunkZ = dataStream.readInt(); + ChunkPos chunkPos = ChunkPos.of(chunkX, chunkZ); + byte[] chunkData = new byte[dataStream.readInt()]; + dataStream.read(chunkData); + map.put(chunkPos, chunkData); + } + return world.restoreRegion(pos, map); + } + + private byte[] serializeRegion(CustomCropsRegion region) { + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); + DataOutputStream outStream = new DataOutputStream(outByteStream); + try { + outStream.writeByte(REGION_VERSION); + outStream.writeInt(region.regionPos().x()); + outStream.writeInt(region.regionPos().z()); + Map map = region.dataToSave(); + outStream.writeInt(map.size()); + for (Map.Entry entry : map.entrySet()) { + outStream.writeInt(entry.getKey().x()); + outStream.writeInt(entry.getKey().z()); + byte[] dataArray = entry.getValue(); + outStream.writeInt(dataArray.length); + outStream.write(dataArray); + } + } catch (IOException e) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to serialize CustomCrops region data." + region.regionPos(), e); + } + return outByteStream.toByteArray(); + } + + private CustomCropsChunk deserializeChunk(CustomCropsWorld world, DataInputStream dataStream) throws IOException { + int chunkVersion = dataStream.readByte(); + byte[] blockData = readCompressedBytes(dataStream); + return deserializeChunk(world, blockData, chunkVersion); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private byte[] readCompressedBytes(DataInputStream dataStream) throws IOException { + int compressedLength = dataStream.readInt(); + int decompressedLength = dataStream.readInt(); + byte[] compressedData = new byte[compressedLength]; + byte[] decompressedData = new byte[decompressedLength]; + + dataStream.read(compressedData); + zstdDecompress(decompressedData, compressedData); + return decompressedData; + } + + private File getWorldFolder(World world) { + return worldFolderProvider.apply(world); + } + + private File getRegionDataFile(World world, RegionPos regionPos) { + return regionFileProvider.apply(world, regionPos); + } + + private String getRegionDataFile(RegionPos regionPos) { + return "r." + regionPos.x() + "." + regionPos.z() + ".mcc"; + } + + private byte[] serializeChunk(SerializableChunk serializableChunk) { + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); + DataOutputStream outStream = new DataOutputStream(outByteStream); + try { + outStream.writeByte(CHUNK_VERSION); + byte[] serializedSections = toBytes(serializableChunk); + byte[] compressed = zstdCompress(serializedSections); + outStream.writeInt(compressed.length); + outStream.writeInt(serializedSections.length); + outStream.write(compressed); + } catch (IOException e) { + BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to serialize chunk " + ChunkPos.of(serializableChunk.x(), serializableChunk.z())); + } + return outByteStream.toByteArray(); + } + + private byte[] toBytes(SerializableChunk chunk) throws IOException { + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384); + DataOutputStream outStream = new DataOutputStream(outByteStream); + outStream.writeInt(chunk.x()); + outStream.writeInt(chunk.z()); + outStream.writeInt(chunk.loadedSeconds()); + outStream.writeLong(chunk.lastLoadedTime()); + // write queue + int[] queue = chunk.queuedTasks(); + outStream.writeInt(queue.length / 2); + for (int i : queue) { + outStream.writeInt(i); + } + // write ticked blocks + int[] tickedSet = chunk.ticked(); + outStream.writeInt(tickedSet.length); + for (int i : tickedSet) { + outStream.writeInt(i); + } + // write block data + List sectionsToSave = chunk.sections(); + outStream.writeInt(sectionsToSave.size()); + for (SerializableSection section : sectionsToSave) { + outStream.writeInt(section.sectionID()); + byte[] blockData = toBytes(section.blocks()); + outStream.writeInt(blockData.length); + outStream.write(blockData); + } + return outByteStream.toByteArray(); + } + + private byte[] toBytes(Collection blocks) throws IOException { + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384); + DataOutputStream outStream = new DataOutputStream(outByteStream); + outStream.writeInt(blocks.size()); + for (CompoundTag block : blocks) { + byte[] blockData = toBytes(block); + outStream.writeInt(blockData.length); + outStream.write(blockData); + } + return outByteStream.toByteArray(); + } + + private byte[] toBytes(CompoundTag tag) throws IOException { + if (tag == null || tag.getValue().isEmpty()) + return new byte[0]; + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); + NBTOutputStream outStream = new NBTOutputStream( + outByteStream, + NBTInputStream.NO_COMPRESSION, + ByteOrder.BIG_ENDIAN + ); + outStream.writeTag(tag); + return outByteStream.toByteArray(); + } + + @SuppressWarnings("all") + private CustomCropsChunk deserializeChunk(CustomCropsWorld world, byte[] bytes, int chunkVersion) throws IOException { + Function keyFunction = chunkVersion < 2 ? + (s) -> { + return Key.key("customcrops", StringUtils.toLowerCase(s)); + } : s -> { + return Key.key(s); + }; + DataInputStream chunkData = new DataInputStream(new ByteArrayInputStream(bytes)); + // read coordinate + int x = chunkData.readInt(); + int z = chunkData.readInt(); + ChunkPos coordinate = new ChunkPos(x, z); + // read loading info + int loadedSeconds = chunkData.readInt(); + long lastLoadedTime = chunkData.readLong(); + // read task queue + int tasksSize = chunkData.readInt(); + PriorityQueue queue = new PriorityQueue<>(Math.max(11, tasksSize)); + for (int i = 0; i < tasksSize; i++) { + int time = chunkData.readInt(); + BlockPos pos = new BlockPos(chunkData.readInt()); + queue.add(new DelayedTickTask(time, pos)); + } + // read ticked blocks + int tickedSize = chunkData.readInt(); + HashSet tickedSet = new HashSet<>(Math.max(11, tickedSize)); + for (int i = 0; i < tickedSize; i++) { + tickedSet.add(new BlockPos(chunkData.readInt())); + } + // read block data + ConcurrentHashMap sectionMap = new ConcurrentHashMap<>(); + int sections = chunkData.readInt(); + // read sections + for (int i = 0; i < sections; i++) { + ConcurrentHashMap blockMap = new ConcurrentHashMap<>(); + int sectionID = chunkData.readInt(); + byte[] sectionBytes = new byte[chunkData.readInt()]; + chunkData.read(sectionBytes); + DataInputStream sectionData = new DataInputStream(new ByteArrayInputStream(sectionBytes)); + int blockAmount = sectionData.readInt(); + // read blocks + for (int j = 0; j < blockAmount; j++){ + byte[] blockData = new byte[sectionData.readInt()]; + sectionData.read(blockData); + CompoundMap block = readCompound(blockData).getValue(); + Key key = keyFunction.apply((String) block.get("type").getValue()); + CompoundMap data = (CompoundMap) block.get("data").getValue(); + CustomCropsBlock customBlock = Registries.BLOCK.get(key); + if (customBlock == null) { + BukkitCustomCropsPlugin.getInstance().getInstance().getPluginLogger().warn("[" + world.worldName() + "] Unrecognized custom block " + key + " has been removed from chunk " + ChunkPos.of(x, z)); + continue; + } + for (int pos : (int[]) block.get("pos").getValue()) { + BlockPos blockPos = new BlockPos(pos); + blockMap.put(blockPos, CustomCropsBlockState.create(customBlock, data)); + } + } + sectionMap.put(sectionID, CustomCropsSection.restore(sectionID, blockMap)); + } + return world.restoreChunk(coordinate, loadedSeconds, lastLoadedTime, sectionMap, queue, tickedSet); + } + + private CompoundTag readCompound(byte[] bytes) throws IOException { + if (bytes.length == 0) + return null; + NBTInputStream nbtInputStream = new NBTInputStream( + new ByteArrayInputStream(bytes), + NBTInputStream.NO_COMPRESSION, + ByteOrder.BIG_ENDIAN + ); + return (CompoundTag) nbtInputStream.readTag(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/BukkitItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemFactory.java similarity index 87% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/BukkitItemFactory.java rename to plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemFactory.java index efa092bdb..7b8ce3942 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/BukkitItemFactory.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemFactory.java @@ -1,9 +1,9 @@ -package net.momirealms.customcrops.mechanic.item.factory; +package net.momirealms.customcrops.bukkit.item; import com.saicone.rtag.RtagItem; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.mechanic.item.factory.impl.ComponentItemFactory; -import net.momirealms.customcrops.mechanic.item.factory.impl.UniversalItemFactory; +import net.momirealms.customcrops.common.item.Item; +import net.momirealms.customcrops.common.item.ItemFactory; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; import org.bukkit.inventory.ItemStack; import java.util.Objects; @@ -83,4 +83,9 @@ protected ItemStack getItem(RtagItem item) { protected ItemStack loadCopy(RtagItem item) { return item.loadCopy(); } + + @Override + protected boolean unbreakable(RtagItem item) { + return item.isUnbreakable(); + } } 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 new file mode 100644 index 000000000..818ffa5f6 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemManager.java @@ -0,0 +1,442 @@ +package net.momirealms.customcrops.bukkit.item; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.item.CustomCropsItem; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedInteractAirEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; +import net.momirealms.customcrops.api.integration.ItemProvider; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.api.util.PluginUtils; +import net.momirealms.customcrops.api.util.StringUtils; +import net.momirealms.customcrops.common.item.Item; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Constructor; +import java.util.*; + +import static java.util.Objects.requireNonNull; + +public class BukkitItemManager extends AbstractItemManager { + + private final BukkitCustomCropsPlugin plugin; + private CustomItemProvider provider; + private AbstractCustomEventListener eventListener; + private final HashMap itemProviders = new HashMap<>(); + private ItemProvider[] itemDetectArray = new ItemProvider[0]; + private final BukkitItemFactory factory; + + public BukkitItemManager(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + try { + this.hookDefaultPlugins(); + } catch (ReflectiveOperationException e) { + plugin.getPluginLogger().warn("Failed to load CustomItemProvider", e); + } + if (this.provider == null) { + plugin.getPluginLogger().warn("ItemsAdder/Oraxen are not installed, which can cause problems unless you use the CustomCrops API."); + } + this.factory = BukkitItemFactory.create(plugin); + } + + @Override + public void setCustomEventListener(@NotNull AbstractCustomEventListener listener) { + Objects.requireNonNull(listener, "listener cannot be null"); + if (this.eventListener != null) { + HandlerList.unregisterAll(this.eventListener); + } + this.eventListener = listener; + Bukkit.getPluginManager().registerEvents(this.eventListener, plugin.getBoostrap()); + plugin.debug("Custom event listener set to " + listener.getClass().getName()); + } + + @Override + public void setCustomItemProvider(@NotNull CustomItemProvider provider) { + Objects.requireNonNull(provider, "provider cannot be null"); + this.provider = provider; + plugin.debug("Custom item provider set to " + provider.getClass().getName()); + } + + public boolean registerItemProvider(ItemProvider item) { + if (itemProviders.containsKey(item.identifier())) return false; + itemProviders.put(item.identifier(), item); + this.resetItemDetectionOrder(); + return true; + } + + public boolean unregisterItemProvider(String id) { + boolean success = itemProviders.remove(id) != null; + if (success) + this.resetItemDetectionOrder(); + return success; + } + + private void resetItemDetectionOrder() { + ArrayList list = new ArrayList<>(); + for (String plugin : ConfigManager.itemDetectOrder()) { + ItemProvider provider = itemProviders.get(plugin); + if (provider != null) + list.add(provider); + } + this.itemDetectArray = list.toArray(new ItemProvider[0]); + } + + private void hookDefaultPlugins() throws ReflectiveOperationException { + if (PluginUtils.isEnabled("Oraxen")) { + String rVersion; + if (PluginUtils.getPluginVersion("Oraxen").startsWith("2")) { + rVersion = "r2"; + } else { + rVersion = "r1"; + } + Class oraxenProviderClass = Class.forName("net.momirealms.customcrops.bukkit.integration.custom.oraxen_" + rVersion + ".OraxenProvider"); + Constructor oraxenProviderConstructor = oraxenProviderClass.getDeclaredConstructor(); + oraxenProviderConstructor.setAccessible(true); + this.provider = (CustomItemProvider) oraxenProviderConstructor.newInstance(); + + Class oraxenListenerClass = Class.forName("net.momirealms.customcrops.bukkit.integration.custom.oraxen_" + rVersion + ".OraxenListener"); + Constructor oraxenListenerConstructor = oraxenListenerClass.getDeclaredConstructor(AbstractItemManager.class); + oraxenListenerConstructor.setAccessible(true); + this.setCustomEventListener((AbstractCustomEventListener) oraxenListenerConstructor.newInstance(this)); + } else if (PluginUtils.isEnabled("ItemsAdder")) { + String rVersion = "r1"; + Class itemsAdderProviderClass = Class.forName("net.momirealms.customcrops.bukkit.integration.custom.itemsadder_" + rVersion + ".ItemsAdderProvider"); + Constructor itemsAdderProviderConstructor = itemsAdderProviderClass.getDeclaredConstructor(); + itemsAdderProviderConstructor.setAccessible(true); + this.provider = (CustomItemProvider) itemsAdderProviderConstructor.newInstance(); + + Class itemsAdderListenerClass = Class.forName("net.momirealms.customcrops.bukkit.integration.custom.itemsadder_" + rVersion + ".ItemsAdderListener"); + Constructor itemsAdderListenerConstructor = itemsAdderListenerClass.getDeclaredConstructor(AbstractItemManager.class); + itemsAdderListenerConstructor.setAccessible(true); + this.setCustomEventListener((AbstractCustomEventListener) itemsAdderListenerConstructor.newInstance(this)); + } + } + + @Override + public void place(@NotNull Location location, @NotNull ExistenceForm form, @NotNull String id, FurnitureRotation rotation) { + switch (form) { + case BLOCK -> placeBlock(location, id); + case FURNITURE -> placeFurniture(location, id, rotation); + case ANY -> throw new IllegalArgumentException("Invalid existence form: " + form); + } + } + + @Override + public FurnitureRotation remove(@NotNull Location location, @NotNull ExistenceForm form) { + switch (form) { + case BLOCK -> { + this.removeBlock(location); + return FurnitureRotation.NONE; + } + case FURNITURE -> { + return this.removeFurniture(location); + } + case ANY -> { + this.removeBlock(location); + return this.removeFurniture(location); + } + } + return FurnitureRotation.NONE; + } + + @Override + public void placeBlock(@NotNull Location location, @NotNull String id) { + if (StringUtils.isCapitalLetter(id)) { + location.getWorld().getBlockAt(location).setType(Material.valueOf(id), false); + } else { + this.provider.placeCustomBlock(location, id); + } + } + + @Override + public void placeFurniture(@NotNull Location location, @NotNull String id, FurnitureRotation rotation) { + Entity entity = this.provider.placeFurniture(location, id); + if (entity != null) { + if (entity instanceof ItemFrame itemFrame) { + itemFrame.setRotation(rotation.getBukkitRotation()); + } else if (entity instanceof LivingEntity livingEntity) { + livingEntity.setRotation(rotation.getYaw(), 0); + } + } + } + + @Override + public void removeBlock(@NotNull Location location) { + if (!this.provider.removeCustomBlock(location)) { + location.getBlock().setType(Material.AIR, false); + } + } + + @Override + public FurnitureRotation removeFurniture(@NotNull Location location) { + Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toSurfaceCenterLocation(location), 0.5,0.25,0.5); + FurnitureRotation rotation = null; + for (Entity entity : entities) { + if (this.provider.removeFurniture(entity) && rotation == null) { + if (entity instanceof ItemFrame itemFrame) { + rotation = FurnitureRotation.getByRotation(itemFrame.getRotation()); + } else { + rotation = FurnitureRotation.getByYaw(entity.getYaw()); + } + } + } + return rotation; + } + + @NotNull + @Override + public String blockID(@NotNull Block block) { + String id = this.provider.blockID(block); + if (id == null) { + id = block.getType().toString(); + } + return id; + } + + @Nullable + @Override + public String furnitureID(@NotNull Entity entity) { + return this.provider.furnitureID(entity); + } + + @Override + @NotNull + public String entityID(@NotNull Entity entity) { + String id = furnitureID(entity); + if (id == null) { + id = entity.getType().toString(); + } + return id; + } + + @Override + @Nullable + public String furnitureID(Location location) { + Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toSurfaceCenterLocation(location), 0.5,0.25,0.5); + for (Entity entity : entities) { + if (provider.isFurniture(entity)) { + return provider.furnitureID(entity); + } + } + return null; + } + + @NotNull + @Override + public String anyID(Location location) { + Block block = location.getBlock(); + if (block.getType() == Material.AIR) { + String id = furnitureID(location); + if (id == null) { + return "AIR"; + } + return id; + } else { + return blockID(location); + } + } + + @Override + public @Nullable String id(Location location, ExistenceForm form) { + return switch (form) { + case BLOCK -> blockID(location); + case FURNITURE -> furnitureID(location); + case ANY -> anyID(location); + }; + } + + @NotNull + @Override + public String id(@Nullable ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR) return "AIR"; + String id = provider.itemID(itemStack); + if (id != null) return id; + for (ItemProvider p : itemDetectArray) { + id = p.itemID(itemStack); + if (id != null) return p.identifier() + ":" + id; + } + return itemStack.getType().name(); + } + + @Nullable + @Override + public ItemStack build(Player player, @NotNull String id) { + ItemStack itemStack = provider.itemStack(player, id); + if (itemStack != null) { + return itemStack; + } + if (!id.contains(":")) { + try { + return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); + } catch (IllegalArgumentException e) { + plugin.getPluginLogger().severe("Item " + id + " not exists", e); + return new ItemStack(Material.PAPER); + } + } else { + String[] split = id.split(":", 2); + ItemProvider provider = requireNonNull(itemProviders.get(split[0]), "Item provider: " + split[0] + " not found"); + return requireNonNull(provider.buildItem(player, split[1]), "Item: " + split[0] + " not found"); + } + } + + @Override + public Item wrap(ItemStack itemStack) { + return factory.wrap(itemStack); + } + + @Override + public void decreaseDamage(Player player, ItemStack itemStack, int amount) { + if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) + return; + Item wrapped = factory.wrap(itemStack); + if (wrapped.unbreakable()) return; + wrapped.damage(Math.max(0, wrapped.damage().orElse(0) - amount)); + wrapped.load(); + } + + @Override + public void increaseDamage(Player player, ItemStack itemStack, int amount) { + if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) + return; + Item wrapped = factory.wrap(itemStack); + if (wrapped.unbreakable()) + return; + ItemMeta previousMeta = itemStack.getItemMeta().clone(); + PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(player, itemStack, amount); + if (EventUtils.fireAndCheckCancel(itemDamageEvent)) { + plugin.debug("Another plugin modified the item from `PlayerItemDamageEvent` called by CustomCrops"); + return; + } + if (!itemStack.getItemMeta().equals(previousMeta)) { + return; + } + int damage = wrapped.damage().orElse(0); + if (damage + amount >= wrapped.maxDamage().orElse((int) itemStack.getType().getMaxDurability())) { + plugin.getSenderFactory().getAudience(player).playSound(Sound.sound(Key.key("minecraft:entity.item.break"), Sound.Source.PLAYER, 1, 1)); + itemStack.setAmount(0); + return; + } + wrapped.damage(damage + amount); + wrapped.load(); + } + + @Override + public void handlePlayerInteractAir(Player player, EquipmentSlot hand, ItemStack itemInHand) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + + String itemID = id(itemInHand); + CustomCropsItem customCropsItem = Registries.ITEMS.get(itemID); + if (customCropsItem != null) { + customCropsItem.interactAir(new WrappedInteractAirEvent( + optionalWorld.get(), + player, + hand, + itemInHand, + itemID + )); + } + } + + @Override + public void handlePlayerInteractBlock(Player player, Block block, String blockID, BlockFace blockFace, EquipmentSlot hand, ItemStack itemInHand, Cancellable event) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + + String itemID = id(itemInHand); + CustomCropsWorld world = optionalWorld.get(); + WrappedInteractEvent wrapped = new WrappedInteractEvent(ExistenceForm.BLOCK, player, world, block.getLocation(), blockID, itemInHand, itemID, hand, blockFace, event); + + handleInteractEvent(blockID, itemID, wrapped); + } + + @Override + public void handlePlayerInteractFurniture(Player player, Location location, String furnitureID, EquipmentSlot hand, ItemStack itemInHand, Cancellable event) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + + String itemID = id(itemInHand); + CustomCropsWorld world = optionalWorld.get(); + WrappedInteractEvent wrapped = new WrappedInteractEvent(ExistenceForm.FURNITURE, player, world, location, furnitureID, itemInHand, itemID, hand, null, event); + + handleInteractEvent(furnitureID, itemID, wrapped); + } + + private void handleInteractEvent(String blockID, String itemID, WrappedInteractEvent wrapped) { + CustomCropsItem customCropsItem = Registries.ITEMS.get(itemID); + if (customCropsItem != null) { + InteractionResult result = customCropsItem.interactAt(wrapped); + if (result != InteractionResult.PASS) + return; + } + + if (wrapped.isCancelled()) return; + + CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(blockID); + if (customCropsBlock != null) { + customCropsBlock.onInteract(wrapped); + } + } + + @Override + public void handlePlayerBreak(Player player, Location location, ItemStack itemInHand, String brokenID, Cancellable event) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + + String itemID = id(itemInHand); + CustomCropsWorld world = optionalWorld.get(); + WrappedBreakEvent wrapped = new WrappedBreakEvent(player, null, world, location, brokenID, itemInHand, itemID, BreakReason.BREAK, event); + CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(brokenID); + if (customCropsBlock != null) { + customCropsBlock.onBreak(wrapped); + } + } + + @Override + public void handlePlayerPlace(Player player, Location location, String placedID, EquipmentSlot hand, ItemStack itemInHand, Cancellable event) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + + String itemID = id(itemInHand); + CustomCropsWorld world = optionalWorld.get(); + WrappedPlaceEvent wrapped = new WrappedPlaceEvent(player, world, location, placedID, hand, itemInHand, itemID, event); + CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(placedID); + if (customCropsBlock != null) { + customCropsBlock.onPlace(wrapped); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/ComponentItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/ComponentItemFactory.java similarity index 92% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/ComponentItemFactory.java rename to plugin/src/main/java/net/momirealms/customcrops/bukkit/item/ComponentItemFactory.java index 5266f84e5..fb6ad1133 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/ComponentItemFactory.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/ComponentItemFactory.java @@ -1,10 +1,9 @@ -package net.momirealms.customcrops.mechanic.item.factory.impl; +package net.momirealms.customcrops.bukkit.item; import com.saicone.rtag.RtagItem; import com.saicone.rtag.data.ComponentType; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; -import net.momirealms.customcrops.mechanic.item.factory.ComponentKeys; +import net.momirealms.customcrops.common.item.ComponentKeys; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; import java.util.List; import java.util.Optional; diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/UniversalItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/UniversalItemFactory.java similarity index 89% rename from plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/UniversalItemFactory.java rename to plugin/src/main/java/net/momirealms/customcrops/bukkit/item/UniversalItemFactory.java index 5fb923c6c..5cfc346c1 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/impl/UniversalItemFactory.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/UniversalItemFactory.java @@ -1,10 +1,7 @@ -package net.momirealms.customcrops.mechanic.item.factory.impl; - - +package net.momirealms.customcrops.bukkit.item; import com.saicone.rtag.RtagItem; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; +import net.momirealms.customcrops.common.plugin.CustomCropsPlugin; import java.util.List; import java.util.Optional; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java new file mode 100644 index 000000000..69db25463 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) <2022> + * + * 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.bukkit.misc; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.helper.VersionHelper; +import net.momirealms.customcrops.common.plugin.feature.Reloadable; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customcrops.common.util.Pair; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.feature.entity.FakeEntity; +import net.momirealms.sparrow.heart.feature.entity.armorstand.FakeArmorStand; +import net.momirealms.sparrow.heart.feature.entity.display.FakeTextDisplay; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.ArrayList; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class HologramManager implements Listener, Reloadable { + + private final ConcurrentHashMap hologramMap = new ConcurrentHashMap<>(); + private final BukkitCustomCropsPlugin plugin; + private SchedulerTask cacheCheckTask; + private static HologramManager manager; + + public static HologramManager getInstance() { + return manager; + } + + public HologramManager(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + manager = this; + } + + @Override + public void load() { + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + this.cacheCheckTask = plugin.getScheduler().asyncRepeating(() -> { + ArrayList removed = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (Map.Entry entry : hologramMap.entrySet()) { + Player player = Bukkit.getPlayer(entry.getKey()); + if (player == null || !player.isOnline()) { + removed.add(entry.getKey()); + } else { + entry.getValue().removeOutDated(current, player); + } + } + for (UUID uuid : removed) { + hologramMap.remove(uuid); + } + }, 100, 100, TimeUnit.MILLISECONDS); + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + for (Map.Entry entry : hologramMap.entrySet()) { + Player player = Bukkit.getPlayer(entry.getKey()); + if (player != null && player.isOnline()) { + entry.getValue().removeAll(player); + } + } + if (cacheCheckTask != null) cacheCheckTask.cancel(); + this.hologramMap.clear(); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + this.hologramMap.remove(event.getPlayer().getUniqueId()); + } + + public void showHologram(Player player, Location location, Component component, int millis) { + HologramCache hologramCache = hologramMap.get(player.getUniqueId()); + if (hologramCache != null) { + hologramCache.showHologram(player, location, component, millis); + } else { + hologramCache = new HologramCache(); + hologramCache.showHologram(player, location, component, millis); + hologramMap.put(player.getUniqueId(), hologramCache); + } + } + + public static class HologramCache { + + private final ConcurrentHashMap> cache = new ConcurrentHashMap<>(); + + public void removeOutDated(long current, Player player) { + ArrayList removed = new ArrayList<>(); + for (Map.Entry> entry : cache.entrySet()) { + if (entry.getValue().right() < current) { + entry.getValue().left().destroy(player); + removed.add(entry.getKey()); + } + } + for (Location location : removed) { + cache.remove(location); + } + } + + public void showHologram(Player player, Location location, Component component, int millis) { + Pair pair = cache.get(location); + if (pair != null) { + pair.left().destroy(player); + pair.right(System.currentTimeMillis() + millis); + if (VersionHelper.isVersionNewerThan1_19_4()) { + FakeTextDisplay fakeEntity = SparrowHeart.getInstance().createFakeTextDisplay(location.clone().add(0,1.25,0)); + fakeEntity.name(AdventureHelper.componentToJson(component)); + fakeEntity.rgba(0, 0, 0, 0); + fakeEntity.spawn(player); + pair.left(fakeEntity); + } else { + FakeArmorStand fakeEntity = SparrowHeart.getInstance().createFakeArmorStand(location); + fakeEntity.name(AdventureHelper.componentToJson(component)); + fakeEntity.small(true); + fakeEntity.invisible(true); + fakeEntity.spawn(player); + pair.left(fakeEntity); + } + } else { + long removeTime = System.currentTimeMillis() + millis; + if (VersionHelper.isVersionNewerThan1_19_4()) { + FakeTextDisplay fakeEntity = SparrowHeart.getInstance().createFakeTextDisplay(location.clone().add(0,1.25,0)); + fakeEntity.name(AdventureHelper.componentToJson(component)); + fakeEntity.rgba(0, 0, 0, 0); + fakeEntity.spawn(player); + this.cache.put(location, Pair.of(fakeEntity, removeTime)); + } else { + FakeArmorStand fakeEntity = SparrowHeart.getInstance().createFakeArmorStand(location); + fakeEntity.name(AdventureHelper.componentToJson(component)); + fakeEntity.small(true); + fakeEntity.invisible(true); + fakeEntity.spawn(player); + this.cache.put(location, Pair.of(fakeEntity, removeTime)); + } + } + } + + public void removeAll(Player player) { + for (Map.Entry> entry : this.cache.entrySet()) { + entry.getValue().left().destroy(player); + } + cache.clear(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/BlockRequirementManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/BlockRequirementManager.java new file mode 100644 index 000000000..23e247d64 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/BlockRequirementManager.java @@ -0,0 +1,52 @@ +package net.momirealms.customcrops.bukkit.requirement; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.requirement.AbstractRequirementManager; + +public class BlockRequirementManager extends AbstractRequirementManager { + + public BlockRequirementManager(BukkitCustomCropsPlugin plugin) { + super(plugin, CustomCropsBlockState.class); + } + + @Override + protected void registerBuiltInRequirements() { + super.registerBuiltInRequirements(); + this.registerPointCondition(); + } + + @Override + public void load() { + loadExpansions(CustomCropsBlockState.class); + } + + private void registerPointCondition() { + registerRequirement((args, actions, runActions) -> { + int value = (int) args; + return (context) -> { + CustomCropsBlockState state = context.holder(); + if (state.type() instanceof CropBlock cropBlock) { + int point = cropBlock.point(state); + if (point > value) return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "point_more_than", "point-more-than"); + registerRequirement((args, actions, runActions) -> { + int value = (int) args; + return (context) -> { + CustomCropsBlockState state = context.holder(); + if (state.type() instanceof CropBlock cropBlock) { + int point = cropBlock.point(state); + if (point < value) return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "point_less_than", "point-less-than"); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java new file mode 100644 index 000000000..204925599 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java @@ -0,0 +1,247 @@ +package net.momirealms.customcrops.bukkit.requirement; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.integration.LevelerProvider; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.requirement.AbstractRequirementManager; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.bukkit.integration.VaultHook; +import net.momirealms.customcrops.common.util.ListUtils; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.List; +import java.util.Locale; + +public class PlayerRequirementManager extends AbstractRequirementManager { + + public PlayerRequirementManager(BukkitCustomCropsPlugin plugin) { + super(plugin, Player.class); + } + + @Override + protected void registerBuiltInRequirements() { + super.registerBuiltInRequirements(); + this.registerItemInHandRequirement(); + this.registerPermissionRequirement(); + this.registerPluginLevelRequirement(); + this.registerCoolDownRequirement(); + this.registerLevelRequirement(); + this.registerMoneyRequirement(); + this.registerPotionEffectRequirement(); + this.registerSneakRequirement(); + this.registerGameModeRequirement(); + } + + @Override + public void load() { + loadExpansions(Player.class); + } + + private void registerItemInHandRequirement() { + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + boolean mainOrOff = section.getString("hand","main").equalsIgnoreCase("main"); + int amount = section.getInt("amount", 1); + List items = ListUtils.toList(section.get("item")); + return context -> { + if (context.holder() == null) return true; + ItemStack itemStack = mainOrOff ? + context.holder().getInventory().getItemInMainHand() + : context.holder().getInventory().getItemInOffHand(); + String id = plugin.getItemManager().id(itemStack); + if (items.contains(id) && itemStack.getAmount() >= amount) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at item-in-hand requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "item-in-hand"); + } + + private void registerPluginLevelRequirement() { + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + String pluginName = section.getString("plugin"); + int level = section.getInt("level"); + String target = section.getString("target"); + return context -> { + if (context.holder() == null) return true; + LevelerProvider levelerProvider = plugin.getIntegrationManager().getLevelerProvider(pluginName); + if (levelerProvider == null) { + plugin.getPluginLogger().warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation."); + return true; + } + if (levelerProvider.getLevel(context.holder(), target) >= level) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plugin-level requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "plugin-level"); + } + + private void registerLevelRequirement() { + registerRequirement((args, actions, runActions) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return true; + int current = context.holder().getLevel(); + if (current >= value.evaluate(context, true)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "level"); + } + + private void registerMoneyRequirement() { + registerRequirement((args, actions, runActions) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (context.holder() == null) return true; + double current = VaultHook.getBalance(context.holder()); + if (current >= value.evaluate(context, true)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "money"); + } + + private void registerCoolDownRequirement() { + registerRequirement((args, actions, runActions) -> { + if (args instanceof Section section) { + String key = section.getString("key"); + int time = section.getInt("time"); + return context -> { + if (context.holder() == null) return true; + if (!plugin.getCoolDownManager().isCoolDown(context.holder().getUniqueId(), key, time)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at cooldown requirement which is expected be `Section`"); + return Requirement.empty(); + } + }, "cooldown"); + } + + private void registerPermissionRequirement() { + registerRequirement((args, actions, runActions) -> { + List perms = ListUtils.toList(args); + return context -> { + if (context.holder() == null) return true; + for (String perm : perms) + if (context.holder().hasPermission(perm)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "permission"); + registerRequirement((args, actions, runActions) -> { + List perms = ListUtils.toList(args); + return context -> { + if (context.holder() == null) return true; + for (String perm : perms) + if (context.holder().hasPermission(perm)) { + if (runActions) ActionManager.trigger(context, actions); + return false; + } + return true; + }; + }, "!permission"); + } + + @SuppressWarnings("deprecation") + private void registerPotionEffectRequirement() { + registerRequirement((args, actions, runActions) -> { + String potions = (String) args; + String[] split = potions.split("(<=|>=|<|>|==)", 2); + PotionEffectType type = PotionEffectType.getByName(split[0]); + if (type == null) { + plugin.getPluginLogger().warn("Potion effect doesn't exist: " + split[0]); + return Requirement.empty(); + } + int required = Integer.parseInt(split[1]); + String operator = potions.substring(split[0].length(), potions.length() - split[1].length()); + return context -> { + if (context.holder() == null) return true; + int level = -1; + PotionEffect potionEffect = context.holder().getPotionEffect(type); + if (potionEffect != null) { + level = potionEffect.getAmplifier(); + } + boolean result = false; + switch (operator) { + case ">=" -> { + if (level >= required) result = true; + } + case ">" -> { + if (level > required) result = true; + } + case "==" -> { + if (level == required) result = true; + } + case "!=" -> { + if (level != required) result = true; + } + case "<=" -> { + if (level <= required) result = true; + } + case "<" -> { + if (level < required) result = true; + } + } + if (result) { + return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }, "potion-effect"); + } + + private void registerSneakRequirement() { + registerRequirement((args, actions, advanced) -> { + boolean sneak = (boolean) args; + return context -> { + if (context.holder() == null) return true; + if (sneak) { + if (context.holder().isSneaking()) + return true; + } else { + if (!context.holder().isSneaking()) + return true; + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "sneak"); + } + + protected void registerGameModeRequirement() { + registerRequirement((args, actions, advanced) -> { + List modes = ListUtils.toList(args); + return context -> { + if (context.holder() == null) return true; + var name = context.holder().getGameMode().name().toLowerCase(Locale.ENGLISH); + if (modes.contains(name)) { + return true; + } + if (advanced) ActionManager.trigger(context, actions); + return false; + }; + }, "gamemode"); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/BukkitSchedulerAdapter.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/BukkitSchedulerAdapter.java new file mode 100644 index 000000000..b475ecaef --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/BukkitSchedulerAdapter.java @@ -0,0 +1,53 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.bukkit.scheduler; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.bukkit.scheduler.impl.BukkitExecutor; +import net.momirealms.customcrops.bukkit.scheduler.impl.FoliaExecutor; +import net.momirealms.customcrops.common.helper.VersionHelper; +import net.momirealms.customcrops.common.plugin.scheduler.AbstractJavaScheduler; +import net.momirealms.customcrops.common.plugin.scheduler.RegionExecutor; +import org.bukkit.Location; +import org.bukkit.World; + +public class BukkitSchedulerAdapter extends AbstractJavaScheduler { + protected RegionExecutor sync; + + public BukkitSchedulerAdapter(BukkitCustomCropsPlugin plugin) { + super(plugin); + if (VersionHelper.isFolia()) { + this.sync = new FoliaExecutor(plugin.getBoostrap()); + } else { + this.sync = new BukkitExecutor(plugin.getBoostrap()); + } + } + + @Override + public RegionExecutor sync() { + return this.sync; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/DummyTask.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/DummyTask.java new file mode 100644 index 000000000..2c98218f4 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/DummyTask.java @@ -0,0 +1,15 @@ +package net.momirealms.customcrops.bukkit.scheduler; + +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; + +public class DummyTask implements SchedulerTask { + + @Override + public void cancel() { + } + + @Override + public boolean isCancelled() { + return true; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/impl/BukkitExecutor.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/impl/BukkitExecutor.java new file mode 100644 index 000000000..55fff5e9a --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/impl/BukkitExecutor.java @@ -0,0 +1,95 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.bukkit.scheduler.impl; + +import net.momirealms.customcrops.bukkit.scheduler.DummyTask; +import net.momirealms.customcrops.common.plugin.scheduler.RegionExecutor; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + +public class BukkitExecutor implements RegionExecutor { + + private final Plugin plugin; + + public BukkitExecutor(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void run(Runnable r, Location l) { + if (Bukkit.isPrimaryThread()) { + r.run(); + } else { + Bukkit.getScheduler().runTask(plugin, r); + } + } + + @Override + public void run(Runnable r, World world, int x, int z) { + run(r); + } + + @Override + public SchedulerTask runLater(Runnable r, long delayTicks, Location l) { + if (delayTicks == 0) { + if (Bukkit.isPrimaryThread()) { + r.run(); + return new DummyTask(); + } else { + return new BukkitCancellable(Bukkit.getScheduler().runTask(plugin, r)); + } + } + return new BukkitCancellable(Bukkit.getScheduler().runTaskLater(plugin, r, delayTicks)); + } + + @Override + public SchedulerTask runRepeating(Runnable r, long delayTicks, long period, Location l) { + return new BukkitCancellable(Bukkit.getScheduler().runTaskTimer(plugin, r, delayTicks, period)); + } + + public static class BukkitCancellable implements SchedulerTask { + + private final BukkitTask task; + + public BukkitCancellable(BukkitTask task) { + this.task = task; + } + + @Override + public void cancel() { + this.task.cancel(); + } + + @Override + public boolean isCancelled() { + return this.task.isCancelled(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/impl/FoliaExecutor.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/impl/FoliaExecutor.java new file mode 100644 index 000000000..07516d1b3 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/impl/FoliaExecutor.java @@ -0,0 +1,100 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.bukkit.scheduler.impl; + +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import net.momirealms.customcrops.common.plugin.scheduler.RegionExecutor; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.plugin.Plugin; + +import java.util.Optional; + +public class FoliaExecutor implements RegionExecutor { + + private final Plugin plugin; + + public FoliaExecutor(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void run(Runnable r, Location l) { + Optional.ofNullable(l).ifPresentOrElse(loc -> Bukkit.getRegionScheduler().execute(plugin, loc, r), () -> Bukkit.getGlobalRegionScheduler().execute(plugin, r)); + } + + @Override + public void run(Runnable r, World world, int x, int z) { + Bukkit.getRegionScheduler().execute(plugin, world, x, z, r); + } + + @Override + public SchedulerTask runLater(Runnable r, long delayTicks, Location l) { + if (l == null) { + if (delayTicks == 0) { + return new FoliaCancellable(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> r.run(), delayTicks)); + } else { + return new FoliaCancellable(Bukkit.getGlobalRegionScheduler().run(plugin, scheduledTask -> r.run())); + } + } else { + if (delayTicks == 0) { + return new FoliaCancellable(Bukkit.getRegionScheduler().run(plugin, l, scheduledTask -> r.run())); + } else { + return new FoliaCancellable(Bukkit.getRegionScheduler().runDelayed(plugin, l, scheduledTask -> r.run(), delayTicks)); + } + } + } + + @Override + public SchedulerTask runRepeating(Runnable r, long delayTicks, long period, Location l) { + if (l == null) { + return new FoliaCancellable(Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, scheduledTask -> r.run(), delayTicks, period)); + } else { + return new FoliaCancellable(Bukkit.getRegionScheduler().runAtFixedRate(plugin, l, scheduledTask -> r.run(), delayTicks, period)); + } + } + + public static class FoliaCancellable implements SchedulerTask { + + private final ScheduledTask task; + + public FoliaCancellable(ScheduledTask task) { + this.task = task; + } + + @Override + public void cancel() { + this.task.cancel(); + } + + @Override + public boolean isCancelled() { + return this.task.isCancelled(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/sender/BukkitSenderFactory.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/sender/BukkitSenderFactory.java new file mode 100644 index 000000000..d9ee21494 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/sender/BukkitSenderFactory.java @@ -0,0 +1,112 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.bukkit.sender; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.common.sender.Sender; +import net.momirealms.customcrops.common.sender.SenderFactory; +import net.momirealms.customcrops.common.util.Tristate; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.RemoteConsoleCommandSender; +import org.bukkit.entity.Player; + +import java.util.UUID; + +public class BukkitSenderFactory extends SenderFactory { + private final BukkitAudiences audiences; + + public BukkitSenderFactory(BukkitCustomCropsPlugin plugin) { + super(plugin); + this.audiences = BukkitAudiences.create(plugin.getBoostrap()); + } + + @Override + protected String getName(CommandSender sender) { + if (sender instanceof Player) { + return sender.getName(); + } + return Sender.CONSOLE_NAME; + } + + @Override + protected UUID getUniqueId(CommandSender sender) { + if (sender instanceof Player) { + return ((Player) sender).getUniqueId(); + } + return Sender.CONSOLE_UUID; + } + + @Override + public Audience getAudience(CommandSender sender) { + return this.audiences.sender(sender); + } + + @Override + protected void sendMessage(CommandSender sender, Component message) { + // we can safely send async for players and the console - otherwise, send it sync + if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) { + getAudience(sender).sendMessage(message); + } else { + getPlugin().getScheduler().executeSync(() -> getAudience(sender).sendMessage(message)); + } + } + + @Override + protected Tristate getPermissionValue(CommandSender sender, String node) { + if (sender.hasPermission(node)) { + return Tristate.TRUE; + } else if (sender.isPermissionSet(node)) { + return Tristate.FALSE; + } else { + return Tristate.UNDEFINED; + } + } + + @Override + protected boolean hasPermission(CommandSender sender, String node) { + return sender.hasPermission(node); + } + + @Override + protected void performCommand(CommandSender sender, String command) { + getPlugin().getBoostrap().getServer().dispatchCommand(sender, command); + } + + @Override + protected boolean isConsole(CommandSender sender) { + return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender; + } + + @Override + public void close() { + super.close(); + this.audiences.close(); + } +} 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 new file mode 100644 index 000000000..4df51036b --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/world/BukkitWorldManager.java @@ -0,0 +1,327 @@ +package net.momirealms.customcrops.bukkit.world; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.world.*; +import net.momirealms.customcrops.api.core.world.adaptor.WorldAdaptor; +import net.momirealms.customcrops.api.integration.SeasonProvider; +import net.momirealms.customcrops.bukkit.config.BukkitConfigManager; +import net.momirealms.customcrops.bukkit.integration.adaptor.BukkitWorldAdaptor; +import net.momirealms.customcrops.bukkit.integration.adaptor.asp_r1.SlimeWorldAdaptorR1; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.world.*; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class BukkitWorldManager implements WorldManager, Listener { + + private final BukkitCustomCropsPlugin plugin; + private final Set> adaptors = new TreeSet<>(); + private final ConcurrentHashMap> worlds = new ConcurrentHashMap<>(); + private final HashMap worldSettings = new HashMap<>(); + private WorldSetting defaultWorldSetting; + private MatchRule matchRule; + private HashSet worldList; + private SeasonProvider seasonProvider; + + public BukkitWorldManager(BukkitCustomCropsPlugin plugin) { + this.plugin = plugin; + try { + Class.forName("com.infernalsuite.aswm.api.SlimePlugin"); + adaptors.add(new SlimeWorldAdaptorR1(1)); + } catch (ClassNotFoundException ignored) { + } + if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldPlugin")) { + adaptors.add(new SlimeWorldAdaptorR1(2)); + } + this.adaptors.add(new BukkitWorldAdaptor()); + this.seasonProvider = new SeasonProvider() { + @NotNull + @Override + public Season getSeason(@NotNull World world) { + return BukkitWorldManager.this.getWorld(world).map(w -> w.extraData().getSeason()).orElse(Season.DISABLE); + } + @Override + public String identifier() { + return "CustomCrops"; + } + }; + } + + public void seasonProvider(SeasonProvider seasonProvider) { + this.seasonProvider = seasonProvider; + } + + @Override + public Season getSeason(World world) { + if (ConfigManager.syncSeasons()) { + World reference = Bukkit.getWorld(ConfigManager.referenceWorld()); + if (reference != null) { + return seasonProvider.getSeason(reference); + } else { + return Season.DISABLE; + } + } else { + return seasonProvider.getSeason(world); + } + } + + @Override + public int getDate(World world) { + if (ConfigManager.syncSeasons()) { + World reference = Bukkit.getWorld(ConfigManager.referenceWorld()); + if (reference != null) { + return getWorld(reference).map(w -> w.extraData().getDate()).orElse(-1); + } else { + return -1; + } + } else { + return getWorld(world).map(w -> w.extraData().getDate()).orElse(-1); + } + } + + @Override + public void load() { + this.loadConfig(); + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + // load and unload worlds + for (World world : Bukkit.getWorlds()) { + if (isMechanicEnabled(world)) { + loadWorld(world); + } else { + unloadWorld(world); + } + } + } + + private void loadConfig() { + YamlDocument config = BukkitConfigManager.getMainConfig(); + + Section section = config.getSection("worlds"); + if (section == null) { + plugin.getPluginLogger().warn("worlds section should not be null"); + return; + } + + this.matchRule = MatchRule.valueOf(section.getString("mode", "blacklist").toUpperCase(Locale.ENGLISH)); + this.worldList = new HashSet<>(section.getStringList("list")); + + Section settingSection = section.getSection("settings"); + if (settingSection == null) { + plugin.getPluginLogger().warn("worlds.settings section should not be null"); + return; + } + + Section defaultSection = settingSection.getSection("_DEFAULT_"); + if (defaultSection == null) { + plugin.getPluginLogger().warn("worlds.settings._DEFAULT_ section should not be null"); + return; + } + + this.defaultWorldSetting = sectionToWorldSetting(defaultSection); + + Section worldSection = settingSection.getSection("_WORLDS_"); + if (worldSection != null) { + for (Map.Entry entry : worldSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + this.worldSettings.put(entry.getKey(), sectionToWorldSetting(inner)); + } + } + } + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + this.worldSettings.clear(); + } + + @Override + public void disable() { + this.unload(); + for (World world : Bukkit.getWorlds()) { + unloadWorld(world); + } + } + + @Override + public CustomCropsWorld loadWorld(World world) { + Optional> optionalWorld = getWorld(world); + if (optionalWorld.isPresent()) { + CustomCropsWorld customCropsWorld = optionalWorld.get(); + customCropsWorld.setting(Optional.ofNullable(worldSettings.get(world.getName())).orElse(defaultWorldSetting)); + return customCropsWorld; + } + CustomCropsWorld adaptedWorld = adapt(world); + adaptedWorld.setting(Optional.ofNullable(worldSettings.get(world.getName())).orElse(defaultWorldSetting)); + adaptedWorld.setTicking(true); + this.worlds.put(world.getName(), adaptedWorld); + for (Chunk chunk : world.getLoadedChunks()) { + loadLoadedChunk(adaptedWorld, ChunkPos.fromBukkitChunk(chunk)); + } + return adaptedWorld; + } + + // Before using the method, make sure that the bukkit chunk is loaded + public void loadLoadedChunk(CustomCropsWorld world, ChunkPos pos) { + if (world.isChunkLoaded(pos)) return; + Optional customChunk = world.getChunk(pos); + // don't load bukkit chunk again since it has been loaded + customChunk.ifPresent(customCropsChunk -> customCropsChunk.load(false)); + } + + @Override + public boolean unloadWorld(World world) { + CustomCropsWorld removedWorld = worlds.remove(world.getName()); + if (removedWorld == null) { + return false; + } + removedWorld.setTicking(false); + removedWorld.save(); + return true; + } + + @EventHandler + public void onWorldSave(WorldSaveEvent event) { + final World world = event.getWorld(); + getWorld(world).ifPresent(CustomCropsWorld::save); + } + + @EventHandler (priority = EventPriority.HIGH) + public void onWorldLoad(WorldLoadEvent event) { + World world = event.getWorld(); + if (!isMechanicEnabled(world)) return; + loadWorld(world); + } + + @EventHandler (priority = EventPriority.LOW) + public void onWorldUnload(WorldUnloadEvent event) { + World world = event.getWorld(); + if (!isMechanicEnabled(world)) return; + unloadWorld(world); + } + + @EventHandler + public void onChunkUnload(ChunkUnloadEvent event) { + final Chunk chunk = event.getChunk(); + final World world = event.getWorld(); + this.getWorld(world) + .flatMap(customWorld -> customWorld.getLoadedChunk(ChunkPos.fromBukkitChunk(chunk))) + .ifPresent(customChunk -> customChunk.unload(true)); + } + + @EventHandler + public void onChunkLoad(ChunkLoadEvent event) { + final Chunk chunk = event.getChunk(); + final World world = event.getWorld(); + this.getWorld(world).ifPresent(customWorld -> loadLoadedChunk(customWorld, ChunkPos.fromBukkitChunk(chunk))); + } + + @EventHandler + public void onEntitiesLoad(EntitiesLoadEvent event) { + + } + + public boolean isMechanicEnabled(World world) { + if (matchRule == MatchRule.WHITELIST) { + return worldList.contains(world.getName()); + } else if (matchRule == MatchRule.BLACKLIST) { + return !worldList.contains(world.getName()); + } else { + for (String regex : worldList) { + if (world.getName().matches(regex)) { + return true; + } + } + return false; + } + } + + @Override + public Optional> getWorld(World world) { + return getWorld(world.getName()); + } + + @Override + public Optional> getWorld(String world) { + return Optional.ofNullable(worlds.get(world)); + } + + @Override + public boolean isWorldLoaded(World world) { + return worlds.containsKey(world.getName()); + } + + @Override + public Set> adaptors() { + return adaptors; + } + + @Override + public CustomCropsWorld adapt(World world) { + return adapt(world.getName()); + } + + @Override + public CustomCropsWorld adapt(String name) { + for (WorldAdaptor adaptor : adaptors) { + Object world = adaptor.getWorld(name); + if (world != null) { + return adaptor.adapt(world); + } + } + throw new RuntimeException("Unable to adapt world " + name); + } + + public enum MatchRule { + + BLACKLIST, + WHITELIST, + REGEX + } + + private static WorldSetting sectionToWorldSetting(Section section) { + return WorldSetting.of( + section.getBoolean("enable", true), + section.getInt("min-tick-unit", 300), + getRandomTickModeByString(section.getString("crop.mode")), + section.getInt("crop.tick-interval", 1), + getRandomTickModeByString(section.getString("pot.mode")), + section.getInt("pot.tick-interval", 2), + getRandomTickModeByString(section.getString("sprinkler.mode")), + section.getInt("sprinkler.tick-interval", 2), + section.getBoolean("offline-tick.enable", false), + section.getInt("offline-tick.max-offline-seconds", 1200), + section.getBoolean("season.enable", false), + section.getBoolean("season.auto-alternation", false), + section.getInt("season.duration", 28), + section.getInt("crop.max-per-chunk", 128), + section.getInt("pot.max-per-chunk", -1), + section.getInt("sprinkler.max-per-chunk", 32), + section.getInt("random-tick-speed", 0) + ); + } + + private static boolean getRandomTickModeByString(String str) { + if (str == null) { + return false; + } + if (str.equalsIgnoreCase("RANDOM_TICK")) { + return true; + } + if (str.equalsIgnoreCase("SCHEDULED_TICK")) { + return false; + } + throw new IllegalArgumentException("Invalid mode found when loading world settings: " + str); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java deleted file mode 100644 index 73fdd6048..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.integration.LevelInterface; -import net.momirealms.customcrops.api.integration.SeasonInterface; -import net.momirealms.customcrops.api.manager.IntegrationManager; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.compatibility.item.MMOItemsItemImpl; -import net.momirealms.customcrops.compatibility.item.MythicMobsItemImpl; -import net.momirealms.customcrops.compatibility.item.NeigeItemsItemImpl; -import net.momirealms.customcrops.compatibility.item.ZaphkielItemImpl; -import net.momirealms.customcrops.compatibility.level.*; -import net.momirealms.customcrops.compatibility.quest.BattlePassHook; -import net.momirealms.customcrops.compatibility.quest.BetonQuestHook; -import net.momirealms.customcrops.compatibility.quest.ClueScrollsHook; -import net.momirealms.customcrops.compatibility.season.AdvancedSeasonsImpl; -import net.momirealms.customcrops.compatibility.season.InBuiltSeason; -import net.momirealms.customcrops.compatibility.season.RealisticSeasonsImpl; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; - -public class IntegrationManagerImpl implements IntegrationManager { - - private final CustomCropsPlugin plugin; - private final HashMap levelPluginMap; - private SeasonInterface seasonInterface; - - public IntegrationManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.levelPluginMap = new HashMap<>(); - } - - @Override - public void init() { - if (plugin.doesHookedPluginExist("MMOItems")) { - plugin.getItemManager().registerItemLibrary(new MMOItemsItemImpl()); - hookMessage("MMOItems"); - } - if (plugin.doesHookedPluginExist("Zaphkiel")) { - plugin.getItemManager().registerItemLibrary(new ZaphkielItemImpl()); - hookMessage("Zaphkiel"); - } - if (plugin.doesHookedPluginExist("NeigeItems")) { - plugin.getItemManager().registerItemLibrary(new NeigeItemsItemImpl()); - hookMessage("NeigeItems"); - } - if (plugin.doesHookedPluginExist("MythicMobs")) { - plugin.getItemManager().registerItemLibrary(new MythicMobsItemImpl()); - hookMessage("MythicMobs"); - } - if (plugin.doesHookedPluginExist("EcoJobs")) { - registerLevelPlugin("EcoJobs", new EcoJobsImpl()); - hookMessage("EcoJobs"); - } - if (plugin.doesHookedPluginExist("Jobs")) { - registerLevelPlugin("JobsReborn", new JobsRebornImpl()); - hookMessage("JobsReborn"); - } - if (plugin.doesHookedPluginExist("AureliumSkills")) { - registerLevelPlugin("AureliumSkills", new AureliumSkillsImpl()); - hookMessage("AureliumSkills"); - } - if (plugin.doesHookedPluginExist("EcoSkills")) { - registerLevelPlugin("EcoSkills", new EcoSkillsImpl()); - hookMessage("EcoSkills"); - } - if (plugin.doesHookedPluginExist("mcMMO")) { - registerLevelPlugin("mcMMO", new McMMOImpl()); - hookMessage("mcMMO"); - } - if (plugin.doesHookedPluginExist("MMOCore")) { - registerLevelPlugin("MMOCore", new MMOCoreImpl()); - hookMessage("MMOCore"); - } - if (plugin.doesHookedPluginExist("AuraSkills")) { - registerLevelPlugin("AuraSkills", new AuraSkillsImpl()); - hookMessage("AuraSkills"); - } - if (plugin.isHookedPluginEnabled("BattlePass")){ - BattlePassHook battlePassHook = new BattlePassHook(); - battlePassHook.register(); - hookMessage("BattlePass"); - } - if (plugin.isHookedPluginEnabled("ClueScrolls")) { - ClueScrollsHook clueScrollsHook = new ClueScrollsHook(); - clueScrollsHook.register(); - hookMessage("ClueScrolls"); - } - if (plugin.isHookedPluginEnabled("BetonQuest")) { - BetonQuestHook.register(); - hookMessage("BetonQuest"); - } - if (plugin.isHookedPluginEnabled("RealisticSeasons")) { - this.seasonInterface = new RealisticSeasonsImpl(); - hookMessage("RealisticSeasons"); - } else if (plugin.isHookedPluginEnabled("AdvancedSeasons", "1.4", "1.5")) { - this.seasonInterface = new AdvancedSeasonsImpl(); - hookMessage("AdvancedSeasons"); - } else { - this.seasonInterface = new InBuiltSeason(plugin.getWorldManager()); - } - } - - @Override - public void disable() { - - } - - @Override - public boolean registerLevelPlugin(String plugin, LevelInterface level) { - if (levelPluginMap.containsKey(plugin)) return false; - levelPluginMap.put(plugin, level); - return true; - } - - @Override - public boolean unregisterLevelPlugin(String plugin) { - return levelPluginMap.remove(plugin) != null; - } - - private void hookMessage(String plugin) { - LogUtils.info( plugin + " hooked!"); - } - - @Override - @Nullable - public LevelInterface getLevelPlugin(String plugin) { - return levelPluginMap.get(plugin); - } - - @Override - public SeasonInterface getSeasonInterface() { - return seasonInterface; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/VaultHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/VaultHook.java deleted file mode 100644 index 2da91f404..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/VaultHook.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility; - -import net.milkbowl.vault.economy.Economy; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import org.bukkit.plugin.RegisteredServiceProvider; - -public class VaultHook { - - private static Economy economy; - - public static boolean initialize() { - RegisteredServiceProvider rsp = CustomCropsPlugin.getInstance().getServer().getServicesManager().getRegistration(Economy.class); - if (rsp == null) { - return false; - } - economy = rsp.getProvider(); - return true; - } - - public static Economy getEconomy() { - return economy; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java deleted file mode 100644 index 115084822..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility.papi; - -import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.MessageManager; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class CCPapi extends PlaceholderExpansion { - - private final CustomCropsPlugin plugin; - - public CCPapi(CustomCropsPlugin plugin) { - this.plugin = plugin; - } - - public void load() { - super.register(); - } - - public void unload() { - super.unregister(); - } - - @Override - public @NotNull String getIdentifier() { - return "customcrops"; - } - - @Override - public @NotNull String getAuthor() { - return "XiaoMoMi"; - } - - @Override - public @NotNull String getVersion() { - return "3.4"; - } - - @Override - public boolean persist() { - return true; - } - - @Override - public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) { - String[] split = params.split("_", 2); - - Player player = offlinePlayer.getPlayer(); - if (player == null) - return null; - - switch (split[0]) { - case "season" -> { - if (split.length == 1) { - return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(player.getWorld())); - } else { - try { - return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(Bukkit.getWorld(split[1]))); - } catch (NullPointerException e) { - LogUtils.severe("World " + split[1] + " does not exist"); - e.printStackTrace(); - } - } - } - case "date" -> { - if (split.length == 1) { - return String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(player.getWorld())); - } else { - try { - return String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(Bukkit.getWorld(split[1]))); - } catch (NullPointerException e) { - LogUtils.severe("World " + split[1] + " does not exist"); - e.printStackTrace(); - } - } - } - } - - return null; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/ParseUtils.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/ParseUtils.java deleted file mode 100644 index c99a86af2..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/ParseUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; - -public class ParseUtils { - - public static String setPlaceholders(Player player, String text) { - return PlaceholderAPI.setPlaceholders(player, text); - } - - public static String setPlaceholders(OfflinePlayer player, String text) { - return PlaceholderAPI.setPlaceholders(player, text); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java deleted file mode 100644 index 2831efa2d..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BattlePassHook.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility.quest; - -import io.github.battlepass.BattlePlugin; -import io.github.battlepass.api.events.server.PluginReloadEvent; -import net.advancedplugins.bp.impl.actions.ActionRegistry; -import net.advancedplugins.bp.impl.actions.external.executor.ActionQuestExecutor; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.event.CropBreakEvent; -import net.momirealms.customcrops.api.event.CropPlantEvent; -import net.momirealms.customcrops.api.mechanic.item.Crop; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.plugin.java.JavaPlugin; - -public class BattlePassHook implements Listener { - - public BattlePassHook() { - Bukkit.getPluginManager().registerEvents(this, CustomCropsPlugin.get()); - } - - public void register() { - ActionRegistry actionRegistry = BattlePlugin.getPlugin().getActionRegistry(); - actionRegistry.hook("customcrops", BPHarvestCropsQuest::new); - } - - @EventHandler(ignoreCancelled = true) - public void onBattlePassReload(PluginReloadEvent event){ - register(); - } - - private static class BPHarvestCropsQuest extends ActionQuestExecutor { - public BPHarvestCropsQuest(JavaPlugin plugin) { - super(plugin, "customcrops"); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCrop(CropBreakEvent event) { - Player player = event.getPlayer(); - if (player == null) return; - - WorldCrop worldCrop = event.getWorldCrop(); - if (worldCrop == null) return; - String id = worldCrop.getConfig().getStageItemByPoint(worldCrop.getPoint()); - - if (id.contains(":")) { - id = id.split(":")[1]; - } - - // Harvest crops - this.executionBuilder("harvest") - .player(player) - .root(id) - .progress(1) - .buildAndExecute(); - } - - @EventHandler (ignoreCancelled = true) - public void onPlantCrop(CropPlantEvent event){ - Player player = event.getPlayer(); - - Crop crop = event.getCrop(); - - // Harvest crops - this.executionBuilder("plant") - .player(player) - .root(crop.getKey()) - .progress(1) - .buildAndExecute(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BetonQuestHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BetonQuestHook.java deleted file mode 100644 index 21461aecb..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/BetonQuestHook.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility.quest; - -import net.momirealms.customcrops.api.event.CropBreakEvent; -import net.momirealms.customcrops.api.event.CropPlantEvent; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import net.momirealms.customcrops.api.util.LogUtils; -import org.betonquest.betonquest.BetonQuest; -import org.betonquest.betonquest.Instruction; -import org.betonquest.betonquest.api.CountingObjective; -import org.betonquest.betonquest.api.config.quest.QuestPackage; -import org.betonquest.betonquest.api.profiles.OnlineProfile; -import org.betonquest.betonquest.api.profiles.Profile; -import org.betonquest.betonquest.exceptions.InstructionParseException; -import org.betonquest.betonquest.exceptions.QuestRuntimeException; -import org.betonquest.betonquest.instruction.variable.VariableNumber; -import org.betonquest.betonquest.instruction.variable.location.VariableLocation; -import org.betonquest.betonquest.utils.PlayerConverter; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; - -import java.util.Collections; -import java.util.HashSet; - -@SuppressWarnings("DuplicatedCode") -public class BetonQuestHook { - - public static void register() { - BetonQuest.getInstance().registerObjectives("customcrops_harvest", HarvestObjective.class); - BetonQuest.getInstance().registerObjectives("customcrops_plant", PlantObjective.class); - } - - public static class HarvestObjective extends CountingObjective implements Listener { - - private final VariableLocation playerLocation; - private final VariableNumber rangeVar; - private final HashSet crop_ids; - - public HarvestObjective(Instruction instruction) throws InstructionParseException { - super(instruction, "crop_to_harvest"); - crop_ids = new HashSet<>(); - Collections.addAll(crop_ids, instruction.getArray()); - targetAmount = instruction.getVarNum(VariableNumber.NOT_LESS_THAN_ONE_CHECKER); - final QuestPackage pack = instruction.getPackage(); - final String loc = instruction.getOptional("playerLocation"); - final String range = instruction.getOptional("range"); - if (loc != null && range != null) { - playerLocation = new VariableLocation(BetonQuest.getInstance().getVariableProcessor(), pack, loc); - rangeVar = new VariableNumber(BetonQuest.getInstance().getVariableProcessor(), pack, range); - } else { - playerLocation = null; - rangeVar = null; - } - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCrop(CropBreakEvent event) { - if (event.getPlayer() == null) { - return; - } - WorldCrop crop = event.getWorldCrop(); - if (crop == null) return; - - OnlineProfile onlineProfile = PlayerConverter.getID(event.getPlayer()); - if (!containsPlayer(onlineProfile)) { - return; - } - if (isInvalidLocation(event, onlineProfile)) { - return; - } - if (this.crop_ids.contains(crop.getConfig().getStageItemByPoint(crop.getPoint())) && this.checkConditions(onlineProfile)) { - getCountingData(onlineProfile).progress(1); - completeIfDoneOrNotify(onlineProfile); - } - } - - private boolean isInvalidLocation(CropBreakEvent event, final Profile profile) { - if (playerLocation == null || rangeVar == null) { - return false; - } - - final Location targetLocation; - try { - targetLocation = playerLocation.getValue(profile); - } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { - LogUtils.warn(e.getMessage()); - return true; - } - int range; - try { - range = rangeVar.getValue(profile).intValue(); - } catch (QuestRuntimeException e) { - throw new RuntimeException(e); - } - final Location playerLoc = event.getPlayer().getLocation(); - return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range; - } - - @Override - public void start() { - Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance()); - } - - @Override - public void stop() { - HandlerList.unregisterAll(this); - } - } - - public static class PlantObjective extends CountingObjective implements Listener { - - private final VariableLocation playerLocation; - private final VariableNumber rangeVar; - private final HashSet crops; - - public PlantObjective(Instruction instruction) throws InstructionParseException { - super(instruction, "crop_to_plant"); - crops = new HashSet<>(); - Collections.addAll(crops, instruction.getArray()); - targetAmount = instruction.getVarNum(VariableNumber.NOT_LESS_THAN_ONE_CHECKER); - final QuestPackage pack = instruction.getPackage(); - final String loc = instruction.getOptional("playerLocation"); - final String range = instruction.getOptional("range"); - if (loc != null && range != null) { - playerLocation = new VariableLocation(BetonQuest.getInstance().getVariableProcessor(), pack, loc); - rangeVar = new VariableNumber(BetonQuest.getInstance().getVariableProcessor(), pack, range); - } else { - playerLocation = null; - rangeVar = null; - } - } - - @EventHandler (ignoreCancelled = true) - public void onPlantCrop(CropPlantEvent event) { - OnlineProfile onlineProfile = PlayerConverter.getID(event.getPlayer()); - if (!containsPlayer(onlineProfile)) { - return; - } - if (isInvalidLocation(event, onlineProfile)) { - return; - } - if (this.crops.contains(event.getCrop().getKey()) && this.checkConditions(onlineProfile)) { - getCountingData(onlineProfile).progress(1); - completeIfDoneOrNotify(onlineProfile); - } - } - - private boolean isInvalidLocation(CropPlantEvent event, final Profile profile) { - if (playerLocation == null || rangeVar == null) { - return false; - } - - final Location targetLocation; - try { - targetLocation = playerLocation.getValue(profile); - } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { - LogUtils.warn(e.getMessage()); - return true; - } - int range; - try { - range = rangeVar.getValue(profile).intValue(); - } catch (QuestRuntimeException e) { - throw new RuntimeException(e); - } - final Location playerLoc = event.getPlayer().getLocation(); - return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range; - } - - @Override - public void start() { - Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance()); - } - - @Override - public void stop() { - HandlerList.unregisterAll(this); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/ClueScrollsHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/ClueScrollsHook.java deleted file mode 100644 index 997e37914..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/quest/ClueScrollsHook.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility.quest; - -import com.electro2560.dev.cluescrolls.api.*; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.event.CropBreakEvent; -import net.momirealms.customcrops.api.event.CropPlantEvent; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; - -public class ClueScrollsHook implements Listener { - - private final CustomClue harvestClue; - private final CustomClue plantClue; - - public ClueScrollsHook() { - harvestClue = ClueScrollsAPI.getInstance().registerCustomClue(CustomCropsPlugin.getInstance(), "harvest", new ClueConfigData("id", DataType.STRING)); - plantClue = ClueScrollsAPI.getInstance().registerCustomClue(CustomCropsPlugin.getInstance(), "plant", new ClueConfigData("id", DataType.STRING)); - } - - public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomCropsPlugin.get()); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCrop(CropBreakEvent event) { - final Player player = event.getPlayer(); - if (player == null) return; - - WorldCrop crop = event.getWorldCrop(); - if (crop == null) return; - - harvestClue.handle( - player, - 1, - new ClueDataPair("id", crop.getConfig().getStageItemByPoint(crop.getPoint())) - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlantCrop(CropPlantEvent event) { - plantClue.handle( - event.getPlayer(), - 1, - new ClueDataPair("id", event.getCrop().getKey()) - ); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/InBuiltSeason.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/InBuiltSeason.java deleted file mode 100644 index 16a0346ca..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/InBuiltSeason.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility.season; - -import net.momirealms.customcrops.api.integration.SeasonInterface; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.WorldManager; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; -import net.momirealms.customcrops.api.mechanic.world.level.WorldInfoData; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import org.bukkit.World; -import org.jetbrains.annotations.Nullable; - -public class InBuiltSeason implements SeasonInterface { - - private final WorldManager worldManager; - - public InBuiltSeason(WorldManager worldManager) { - this.worldManager = worldManager; - } - - @Override - public @Nullable Season getSeason(World world) { - return worldManager - .getCustomCropsWorld(world) - .map(CustomCropsWorld::getSeason) - .orElse(null); - } - - @Override - public int getDate(World world) { - if (ConfigManager.syncSeasons()) - world = ConfigManager.referenceWorld(); - if (world == null) - return 0; - return worldManager - .getCustomCropsWorld(world) - .map(CustomCropsWorld::getDate) - .orElse(0); - } - - @Override - public void setSeason(World world, Season season) { - worldManager.getCustomCropsWorld(world) - .ifPresent(customWorld -> { - WorldInfoData infoData = customWorld.getInfoData(); - infoData.setSeason(season); - }); - } - - @Override - public void setDate(World world, int date) { - worldManager.getCustomCropsWorld(world) - .ifPresent(customWorld -> { - WorldInfoData infoData = customWorld.getInfoData(); - infoData.setDate(date); - }); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/RealisticSeasonsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/RealisticSeasonsImpl.java deleted file mode 100644 index 970351134..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/RealisticSeasonsImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.compatibility.season; - -import me.casperge.realisticseasons.api.SeasonsAPI; -import me.casperge.realisticseasons.calendar.Date; -import net.momirealms.customcrops.api.integration.SeasonInterface; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import org.bukkit.World; -import org.jetbrains.annotations.Nullable; - -public class RealisticSeasonsImpl implements SeasonInterface { - - private final SeasonsAPI api; - - public RealisticSeasonsImpl() { - this.api = SeasonsAPI.getInstance(); - } - - @Override - public @Nullable Season getSeason(World world) { - return switch (api.getSeason(world)) { - case WINTER -> Season.WINTER; - case SPRING -> Season.SPRING; - case SUMMER -> Season.SUMMER; - case FALL -> Season.AUTUMN; - case DISABLED, RESTORE -> null; - }; - } - - @Override - public int getDate(World world) { - return api.getDate(world).getDay(); - } - - @Override - public void setSeason(World world, Season season) { - me.casperge.realisticseasons.season.Season rsSeason = switch (season) { - case AUTUMN -> me.casperge.realisticseasons.season.Season.FALL; - case SUMMER -> me.casperge.realisticseasons.season.Season.SUMMER; - case WINTER -> me.casperge.realisticseasons.season.Season.WINTER; - case SPRING -> me.casperge.realisticseasons.season.Season.SPRING; - }; - api.setSeason(world, rsSeason); - } - - @Override - public void setDate(World world, int date) { - Date rsDate = api.getDate(world); - api.setDate(world, new Date(date, rsDate.getMonth(), rsDate.getYear())); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java deleted file mode 100644 index 6f6c7e3df..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.momirealms.customcrops.libraries.dependencies; - -import com.google.common.collect.ImmutableList; -import net.momirealms.customcrops.libraries.dependencies.relocation.Relocation; -import org.jetbrains.annotations.Nullable; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.Locale; - -/** - * The dependencies used by CustomCrops. - */ -public enum Dependency { - - ASM( - "org.ow2.asm", - "asm", - "9.7", - null, - "asm" - ), - ASM_COMMONS( - "org.ow2.asm", - "asm-commons", - "9.7", - null, - "asm-commons" - ), - JAR_RELOCATOR( - "me.lucko", - "jar-relocator", - "1.7", - null, - "jar-relocator" - ), - COMMAND_API( - "dev{}jorel", - "commandapi-bukkit-shade", - "9.5.3", - null, - "commandapi-bukkit", - Relocation.of("commandapi", "dev{}jorel{}commandapi") - ), - COMMAND_API_MOJMAP( - "dev{}jorel", - "commandapi-bukkit-shade-mojang-mapped", - "9.5.3", - null, - "commandapi-bukkit-mojang-mapped", - Relocation.of("commandapi", "dev{}jorel{}commandapi") - ), - BOOSTED_YAML( - "dev{}dejvokep", - "boosted-yaml", - "1.3.6", - null, - "boosted-yaml", - Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") - ), - H2_DRIVER( - "com.h2database", - "h2", - "2.2.224", - null, - "h2database" - ), - SQLITE_DRIVER( - "org.xerial", - "sqlite-jdbc", - "3.45.1.0", - null, - "sqlite-jdbc" - ), - SLF4J_SIMPLE( - "org.slf4j", - "slf4j-simple", - "2.0.12", - null, - "slf4j-simple" - ), - SLF4J_API( - "org.slf4j", - "slf4j-api", - "2.0.12", - null, - "slf4j-api" - ), - BSTATS_BASE( - "org{}bstats", - "bstats-base", - "3.0.2", - null, - "bstats-base", - Relocation.of("bstats", "org{}bstats") - ), - BSTATS_BUKKIT( - "org{}bstats", - "bstats-bukkit", - "3.0.2", - null, - "bstats-bukkit", - Relocation.of("bstats", "org{}bstats") - ), - GSON( - "com.google.code.gson", - "gson", - "2.10.1", - null, - "gson" - ), - EXP4J( - "net{}objecthunter", - "exp4j", - "0.4.8", - null, - "exp4j", - Relocation.of("exp4j", "net{}objecthunter{}exp4j") - ); - - private final String mavenRepoPath; - private final String version; - private final List relocations; - private final String repo; - private final String artifact; - - private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; - - Dependency(String groupId, String artifactId, String version, String repo, String artifact) { - this(groupId, artifactId, version, repo, artifact, new Relocation[0]); - } - - Dependency(String groupId, String artifactId, String version, String repo, String artifact, Relocation... relocations) { - this.mavenRepoPath = String.format(MAVEN_FORMAT, - rewriteEscaping(groupId).replace(".", "/"), - rewriteEscaping(artifactId), - version, - rewriteEscaping(artifactId), - version - ); - this.version = version; - this.relocations = ImmutableList.copyOf(relocations); - this.repo = repo; - this.artifact = artifact; - } - - private static String rewriteEscaping(String s) { - return s.replace("{}", "."); - } - - public String getFileName(String classifier) { - String name = artifact.toLowerCase(Locale.ROOT).replace('_', '-'); - String extra = classifier == null || classifier.isEmpty() - ? "" - : "-" + classifier; - return name + "-" + this.version + extra + ".jar"; - } - - String getMavenRepoPath() { - return this.mavenRepoPath; - } - - public List getRelocations() { - return this.relocations; - } - - /** - * Creates a {@link MessageDigest} suitable for computing the checksums - * of dependencies. - * - * @return the digest - */ - public static MessageDigest createDigest() { - try { - return MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - @Nullable - public String getRepo() { - return repo; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/JarInJarClassLoader.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/JarInJarClassLoader.java deleted file mode 100644 index 34b876061..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/JarInJarClassLoader.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.momirealms.customcrops.libraries.loader; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; - -/** - * Classloader that can load a jar from within another jar file. - * - *

The "loader" jar contains the loading code & public API classes, - * and is class-loaded by the platform.

- * - *

The inner "plugin" jar contains the plugin itself, and is class-loaded - * by the loading code & this classloader.

- */ -public class JarInJarClassLoader extends URLClassLoader { - static { - ClassLoader.registerAsParallelCapable(); - } - - /** - * Creates a new jar-in-jar class loader. - * - * @param loaderClassLoader the loader plugin's classloader (setup and created by the platform) - * @param jarResourcePath the path to the jar-in-jar resource within the loader jar - * @throws LoadingException if something unexpectedly bad happens - */ - public JarInJarClassLoader(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException { - super(new URL[]{extractJar(loaderClassLoader, jarResourcePath)}, loaderClassLoader); - } - - public void addJarToClasspath(URL url) { - addURL(url); - } - - public void deleteJarResource() { - URL[] urls = getURLs(); - if (urls.length == 0) { - return; - } - - try { - Path path = Paths.get(urls[0].toURI()); - Files.deleteIfExists(path); - } catch (Exception e) { - // ignore - } - } - - /** - * Creates a new plugin instance. - * - * @param bootstrapClass the name of the bootstrap plugin class - * @param loaderPluginType the type of the loader plugin, the only parameter of the bootstrap - * plugin constructor - * @param loaderPlugin the loader plugin instance - * @param the type of the loader plugin - * @return the instantiated bootstrap plugin - */ - public LoaderBootstrap instantiatePlugin(String bootstrapClass, Class loaderPluginType, T loaderPlugin) throws LoadingException { - Class plugin; - try { - plugin = loadClass(bootstrapClass).asSubclass(LoaderBootstrap.class); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to load bootstrap class", e); - } - - Constructor constructor; - try { - constructor = plugin.getConstructor(loaderPluginType); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to get bootstrap constructor", e); - } - - try { - return constructor.newInstance(loaderPlugin); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to create bootstrap plugin instance", e); - } - } - - /** - * Extracts the "jar-in-jar" from the loader plugin into a temporary file, - * then returns a URL that can be used by the {@link JarInJarClassLoader}. - * - * @param loaderClassLoader the classloader for the "host" loader plugin - * @param jarResourcePath the inner jar resource path - * @return a URL to the extracted file - */ - private static URL extractJar(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException { - // get the jar-in-jar resource - URL jarInJar = loaderClassLoader.getResource(jarResourcePath); - if (jarInJar == null) { - throw new LoadingException("Could not locate jar-in-jar"); - } - - // create a temporary file - // on posix systems by default this is only read/writable by the process owner - Path path; - try { - path = Files.createTempFile("customcrops-jarinjar", ".jar.tmp"); - } catch (IOException e) { - throw new LoadingException("Unable to create a temporary file", e); - } - - // mark that the file should be deleted on exit - path.toFile().deleteOnExit(); - - // copy the jar-in-jar to the temporary file path - try (InputStream in = jarInJar.openStream()) { - Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new LoadingException("Unable to copy jar-in-jar to temporary path", e); - } - - try { - return path.toUri().toURL(); - } catch (MalformedURLException e) { - throw new LoadingException("Unable to get URL from path", e); - } - } - -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java deleted file mode 100644 index 486153ed9..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.title.Title; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.AdventureManager; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.MessageManager; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; - -import java.time.Duration; - -public class AdventureManagerImpl extends AdventureManager { - - private final CustomCropsPlugin plugin; - private BukkitAudiences audiences; - - public AdventureManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - init(); - } - - @Override - public void init() { - this.audiences = BukkitAudiences.create(plugin); - } - - @Override - public void disable() { - if (this.audiences != null) - this.audiences.close(); - } - - @Override - public void sendMessage(CommandSender sender, String text) { - if (text == null) return; - if (sender instanceof Player player) sendPlayerMessage(player, text); - else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(text); - } - - - @Override - public void sendMessageWithPrefix(CommandSender sender, String text) { - if (text == null) return; - if (sender instanceof Player player) sendPlayerMessage(player, MessageManager.prefix() + text); - else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(MessageManager.prefix() + text); - } - - @Override - public void sendConsoleMessage(String text) { - if (text == null) return; - Audience au = audiences.sender(Bukkit.getConsoleSender()); - au.sendMessage(getComponentFromMiniMessage(text)); - } - - @Override - public void sendPlayerMessage(Player player, String text) { - if (player == null) return; - Audience au = audiences.player(player); - au.sendMessage(getComponentFromMiniMessage(text)); - } - - @Override - public void sendActionbar(Player player, String text) { - if (player == null) return; - Audience au = audiences.player(player); - au.sendActionBar(getComponentFromMiniMessage(text)); - } - - @Override - public void sendSound(Player player, Sound.Source source, Key key, float pitch, float volume) { - if (player == null) return; - sendSound(player, Sound.sound(key, source, volume, pitch)); - } - - @Override - public void sendSound(Player player, Sound sound) { - if (player == null) return; - Audience au = audiences.player(player); - au.playSound(sound); - } - - @Override - public void sendTitle(Player player, String title, String subTitle, int fadeIn, int stay, int fadeOut) { - if (player == null) return; - Audience au = audiences.player(player); - au.showTitle(Title.title(getComponentFromMiniMessage(title), getComponentFromMiniMessage(subTitle), Title.Times.times( - Duration.ofMillis(fadeIn * 50L), - Duration.ofMillis(stay * 50L), - Duration.ofMillis(fadeOut * 50L) - ))); - } - - @Override - public Component getComponentFromMiniMessage(String text) { - if (text == null) { - return Component.empty(); - } - if (ConfigManager.legacyColorSupport()) { - return MiniMessage.miniMessage().deserialize(legacyToMiniMessage(text)); - } else { - return MiniMessage.miniMessage().deserialize(text); - } - } - - @Override - public String legacyToMiniMessage(String legacy) { - StringBuilder stringBuilder = new StringBuilder(); - char[] chars = legacy.toCharArray(); - for (int i = 0; i < chars.length; i++) { - if (!isColorCode(chars[i])) { - stringBuilder.append(chars[i]); - continue; - } - if (i + 1 >= chars.length) { - stringBuilder.append(chars[i]); - continue; - } - switch (chars[i+1]) { - case '0' -> stringBuilder.append(""); - case '1' -> stringBuilder.append(""); - case '2' -> stringBuilder.append(""); - case '3' -> stringBuilder.append(""); - case '4' -> stringBuilder.append(""); - case '5' -> stringBuilder.append(""); - case '6' -> stringBuilder.append(""); - case '7' -> stringBuilder.append(""); - case '8' -> stringBuilder.append(""); - case '9' -> stringBuilder.append(""); - case 'a' -> stringBuilder.append(""); - case 'b' -> stringBuilder.append(""); - case 'c' -> stringBuilder.append(""); - case 'd' -> stringBuilder.append(""); - case 'e' -> stringBuilder.append(""); - case 'f' -> stringBuilder.append(""); - case 'r' -> stringBuilder.append(""); - case 'l' -> stringBuilder.append(""); - case 'm' -> stringBuilder.append(""); - case 'o' -> stringBuilder.append(""); - case 'n' -> stringBuilder.append(""); - case 'k' -> stringBuilder.append(""); - case 'x' -> { - if (i + 13 >= chars.length - || !isColorCode(chars[i+2]) - || !isColorCode(chars[i+4]) - || !isColorCode(chars[i+6]) - || !isColorCode(chars[i+8]) - || !isColorCode(chars[i+10]) - || !isColorCode(chars[i+12])) { - stringBuilder.append(chars[i]); - continue; - } - stringBuilder - .append("<#") - .append(chars[i+3]) - .append(chars[i+5]) - .append(chars[i+7]) - .append(chars[i+9]) - .append(chars[i+11]) - .append(chars[i+13]) - .append(">"); - i += 12; - } - default -> { - stringBuilder.append(chars[i]); - continue; - } - } - i++; - } - return stringBuilder.toString(); - } - - @Override - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean isColorCode(char c) { - return c == '§' || c == '&'; - } - - @Override - public int rgbaToDecimal(String rgba) { - String[] split = rgba.split(","); - int r = Integer.parseInt(split[0]); - int g = Integer.parseInt(split[1]); - int b = Integer.parseInt(split[2]); - int a = Integer.parseInt(split[3]); - return (a << 24) | (r << 16) | (g << 8) | b; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java deleted file mode 100644 index ac96dea3d..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import dev.jorel.commandapi.*; -import dev.jorel.commandapi.arguments.ArgumentSuggestions; -import dev.jorel.commandapi.arguments.IntegerArgument; -import dev.jorel.commandapi.arguments.StringArgument; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Initable; -import net.momirealms.customcrops.api.integration.SeasonInterface; -import net.momirealms.customcrops.api.manager.AdventureManager; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.MessageManager; -import net.momirealms.customcrops.api.mechanic.item.ItemType; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsSection; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import net.momirealms.customcrops.compatibility.season.InBuiltSeason; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.generator.WorldInfo; - -import java.util.Locale; -import java.util.Optional; - -public class CommandManager implements Initable { - - private final CustomCropsPlugin plugin; - - public CommandManager(CustomCropsPlugin plugin) { - this.plugin = plugin; - init(); - } - - @Override - public void init() { - if (!CommandAPI.isLoaded()) - CommandAPI.onLoad(new CommandAPIBukkitConfig(plugin).silentLogs(true)); - new CommandAPICommand("customcrops") - .withPermission("customcrops.admin") - .withAliases("ccrops") - .withSubcommands( - getReloadCommand(), - getAboutCommand(), - getSeasonCommand(), - getDateCommand(), - getForceTickCommand(), - getUnsafeCommand() - ) - .register(); - } - - @Override - public void disable() { - CommandAPI.unregister("customcrops"); - } - - private CommandAPICommand getReloadCommand() { - return new CommandAPICommand("reload") - .executes((sender, args) -> { - long time1 = System.currentTimeMillis(); - plugin.reload(); - long time2 = System.currentTimeMillis(); - plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.reloadMessage().replace("{time}", String.valueOf(time2 - time1))); - }); - } - - private CommandAPICommand getUnsafeCommand() { - return new CommandAPICommand("unsafe") - .withSubcommands( - new CommandAPICommand("delete-chunk-data").executesPlayer((player, args) -> { - plugin.getWorldManager().getCustomCropsWorld(player.getWorld()).ifPresent(customCropsWorld -> { - var optionalChunk = customCropsWorld.getLoadedChunkAt(ChunkPos.getByBukkitChunk(player.getChunk())); - if (optionalChunk.isEmpty()) { - AdventureManager.getInstance().sendMessageWithPrefix(player, "This chunk doesn't have any data."); - return; - } - customCropsWorld.deleteChunk(ChunkPos.getByBukkitChunk(player.getChunk())); - AdventureManager.getInstance().sendMessageWithPrefix(player, "Done."); - }); - }), - new CommandAPICommand("check-data").executesPlayer((player, args) -> { - Block block = player.getTargetBlockExact(10); - if (block != null) { - Optional customCropsBlock = plugin.getWorldManager().getBlockAt(SimpleLocation.of(block.getLocation())); - if (customCropsBlock.isPresent()) { - AdventureManager.getInstance().sendMessageWithPrefix(player, customCropsBlock.get().getType() + ":" + customCropsBlock.get().getCompoundMap()); - return; - } - } - AdventureManager.getInstance().sendMessageWithPrefix(player, "Data not found"); - }) - ); - } - - private CommandAPICommand getAboutCommand() { - return new CommandAPICommand("about").executes((sender, args) -> { - plugin.getAdventure().sendMessage(sender, "<#FFA500>⛈ CustomCrops - <#87CEEB>" + CustomCropsPlugin.getInstance().getVersionManager().getPluginVersion()); - plugin.getAdventure().sendMessage(sender, "<#FFFFE0>Ultra-customizable planting experience for Minecraft servers"); - plugin.getAdventure().sendMessage(sender, "<#DA70D6>\uD83E\uDDEA Author: <#FFC0CB>XiaoMoMi"); - plugin.getAdventure().sendMessage(sender, "<#FF7F50>\uD83D\uDD25 Contributors: <#FFA07A>Cha_Shao, <#FFA07A>TopOrigin, <#FFA07A>AmazingCat"); - plugin.getAdventure().sendMessage(sender, "<#FFD700>⭐ Document <#A9A9A9>| <#FAFAD2>⛏ Github <#A9A9A9>| <#48D1CC>\uD83D\uDD14 Polymart"); - }); - } - - private CommandAPICommand getForceTickCommand() { - return new CommandAPICommand("force-tick") - .withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> Bukkit.getWorlds().stream().map(WorldInfo::getName).toList().toArray(new String[0])))) - .withArguments(new StringArgument("type").replaceSuggestions(ArgumentSuggestions.strings("sprinkler", "crop", "pot", "scarecrow", "greenhouse"))) - .executes((sender, args) -> { - String worldName = (String) args.get("world"); - World world = Bukkit.getWorld(worldName); - if (world == null) { - plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); - return; - } - ItemType itemType = ItemType.valueOf(((String) args.get("type")).toUpperCase(Locale.ENGLISH)); - Optional customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world); - if (customCropsWorld.isEmpty()) { - plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); - return; - } - plugin.getScheduler().runTaskAsync(() -> { - for (CustomCropsChunk chunk : customCropsWorld.get().getChunkStorage()) { - for (CustomCropsSection section : chunk.getSections()) { - for (CustomCropsBlock block : section.getBlocks()) { - if (block.getType() == itemType) { - block.tick(1, false); - } - } - } - } - }); - }); - } - - private CommandAPICommand getDateCommand() { - return new CommandAPICommand("date") - .withSubcommands( - new CommandAPICommand("get") - .withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> plugin.getWorldManager().getCustomCropsWorlds().stream() - .filter(customCropsWorld -> customCropsWorld.getWorldSetting().isEnableSeason()) - .map(CustomCropsWorld::getWorldName) - .toList() - .toArray(new String[0])))) - .executes((sender, args) -> { - String worldName = (String) args.get("world"); - World world = Bukkit.getWorld(worldName); - if (world == null) { - plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); - return; - } - plugin.getAdventure().sendMessageWithPrefix(sender, String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(world))); - }), - new CommandAPICommand("set") - .withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> plugin.getWorldManager().getCustomCropsWorlds().stream() - .filter(customCropsWorld -> customCropsWorld.getWorldSetting().isEnableSeason()) - .map(CustomCropsWorld::getWorldName) - .toList() - .toArray(new String[0])))) - .withArguments(new IntegerArgument("date",1)) - .executes((sender, args) -> { - String worldName = (String) args.get("world"); - World world = Bukkit.getWorld(worldName); - if (world == null) { - plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); - return; - } - int date = (int) args.getOrDefault("date", 1); - SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface(); - if (!(seasonInterface instanceof InBuiltSeason inBuiltSeason)) { - plugin.getAdventure().sendMessageWithPrefix(sender, "Detected that you are using a season plugin. Please set date in that plugin."); - return; - } - Optional customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world); - if (customCropsWorld.isEmpty()) { - plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); - return; - } - if (!customCropsWorld.get().getWorldSetting().isEnableSeason()) { - plugin.getAdventure().sendMessageWithPrefix(sender, "Season is not enabled in that world"); - return; - } - if (date > customCropsWorld.get().getWorldSetting().getSeasonDuration()) { - plugin.getAdventure().sendMessageWithPrefix(sender, "Date should be a value no higher than season duration"); - return; - } - String pre = String.valueOf(inBuiltSeason.getDate(world)); - customCropsWorld.get().getInfoData().setDate(date); - plugin.getAdventure().sendMessageWithPrefix(sender, "Date in world("+world.getName()+"): " + pre + " -> " + date); - }) - ); - } - - private CommandAPICommand getSeasonCommand() { - return new CommandAPICommand("season") - .withSubcommands( - new CommandAPICommand("get") - .withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> plugin.getWorldManager().getCustomCropsWorlds().stream() - .filter(customCropsWorld -> customCropsWorld.getWorldSetting().isEnableSeason()) - .map(CustomCropsWorld::getWorldName) - .toList() - .toArray(new String[0])))) - .executes((sender, args) -> { - String worldName = (String) args.get("world"); - World world = Bukkit.getWorld(worldName); - if (world == null) { - plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); - return; - } - plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(world))); - }), - new CommandAPICommand("set") - .withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> { - if (ConfigManager.syncSeasons()) { - return new String[]{ConfigManager.referenceWorld().getName()}; - } - return plugin.getWorldManager().getCustomCropsWorlds().stream() - .filter(customCropsWorld -> customCropsWorld.getWorldSetting().isEnableSeason()) - .map(CustomCropsWorld::getWorldName) - .toList() - .toArray(new String[0]); - }))) - .withArguments(new StringArgument("season") - .replaceSuggestions(ArgumentSuggestions.stringsWithTooltips(info -> - new IStringTooltip[] { - StringTooltip.ofString("Spring", MessageManager.seasonTranslation(Season.SPRING)), - StringTooltip.ofString("Summer", MessageManager.seasonTranslation(Season.SUMMER)), - StringTooltip.ofString("Autumn", MessageManager.seasonTranslation(Season.AUTUMN)), - StringTooltip.ofString("Winter", MessageManager.seasonTranslation(Season.WINTER)) - } - )) - ) - .executes((sender, args) -> { - String worldName = (String) args.get("world"); - World world = Bukkit.getWorld(worldName); - if (world == null) { - plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); - return; - } - String seasonName = (String) args.get("season"); - - SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface(); - if (!(seasonInterface instanceof InBuiltSeason inBuiltSeason)) { - plugin.getAdventure().sendMessageWithPrefix(sender, "Detected that you are using a season plugin. Please set season in that plugin."); - return; - } - String pre = MessageManager.seasonTranslation(inBuiltSeason.getSeason(world)); - Optional customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world); - if (customCropsWorld.isEmpty()) { - plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); - return; - } - if (!customCropsWorld.get().getWorldSetting().isEnableSeason()) { - plugin.getAdventure().sendMessageWithPrefix(sender, "Season is not enabled in that world"); - return; - } - try { - Season season = Season.valueOf(seasonName.toUpperCase(Locale.ENGLISH)); - customCropsWorld.get().getInfoData().setSeason(season); - String next = MessageManager.seasonTranslation(season); - plugin.getAdventure().sendMessageWithPrefix(sender, "Season in world("+world.getName()+"): " + pre + " -> " + next); - } catch (IllegalArgumentException e) { - plugin.getAdventure().sendMessageWithPrefix(sender, "That season doesn't exist"); - } - }) - ); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java deleted file mode 100644 index af6e785e4..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; -import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; -import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; -import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; -import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.Locale; -import java.util.Objects; - -public class ConfigManagerImpl extends ConfigManager { - - public static final String configVersion = "37"; - private final CustomCropsPlugin plugin; - private String lang; - private int maximumPoolSize; - private int corePoolSize; - private int keepAliveTime; - private boolean debug; - private boolean metrics; - private boolean legacyColorSupport; - private boolean protectLore; - private String[] itemDetectionOrder = new String[0]; - private boolean checkUpdate; - private boolean disableMoisture; - private boolean preventTrampling; - private boolean greenhouse; - private boolean scarecrow; - private double[] defaultQualityRatio; - private String greenhouseID; - private String scarecrowID; - private int greenhouseRange; - private int scarecrowRange; - private boolean syncSeasons; - private WeakReference referenceWorld; - private boolean convertWorldOnLoad; - private boolean scarecrowProtectChunk; - private ItemCarrier scarecrowItemType; - private ItemCarrier glassItemType; - - public ConfigManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - } - - @Override - public void load() { - if (!new File(plugin.getDataFolder(), "config.yml").exists()) - ConfigUtils.getConfig("config.yml"); - // update config version - try { - YamlDocument.create( - new File(CustomCropsPlugin.getInstance().getDataFolder(), "config.yml"), - Objects.requireNonNull(CustomCropsPlugin.getInstance().getResource("config.yml")), - GeneralSettings.DEFAULT, - LoaderSettings - .builder() - .setAutoUpdate(true) - .build(), - DumperSettings.DEFAULT, - UpdaterSettings - .builder() - .setVersioning(new BasicVersioning("config-version")) - .addIgnoredRoute(configVersion, "other-settings.placeholder-register", '.') - .build() - ); - } catch (IOException e) { - LogUtils.warn(e.getMessage()); - } - - YamlConfiguration config = ConfigUtils.getConfig("config.yml"); - - debug = config.getBoolean("debug"); - metrics = config.getBoolean("metrics"); - lang = config.getString("lang"); - checkUpdate = config.getBoolean("update-checker", true); - - ConfigurationSection otherSettings = config.getConfigurationSection("other-settings"); - if (otherSettings == null) { - LogUtils.severe("other-settings section should not be null"); - return; - } - - maximumPoolSize = otherSettings.getInt("thread-pool-settings.maximumPoolSize", 10); - corePoolSize = otherSettings.getInt("thread-pool-settings.corePoolSize", 10); - keepAliveTime = otherSettings.getInt("thread-pool-settings.keepAliveTime", 30); - itemDetectionOrder = otherSettings.getStringList("item-detection-order").toArray(new String[0]); - protectLore = otherSettings.getBoolean("protect-original-lore", false); - legacyColorSupport = otherSettings.getBoolean("legacy-color-code-support", true); - convertWorldOnLoad = otherSettings.getBoolean("convert-on-world-load", false); - - ConfigurationSection mechanics = config.getConfigurationSection("mechanics"); - if (mechanics == null) { - LogUtils.severe("mechanics section should not be null"); - return; - } - - defaultQualityRatio = ConfigUtils.getQualityRatio(mechanics.getString("default-quality-ratio", "17/2/1")); - disableMoisture = mechanics.getBoolean("vanilla-farmland.disable-moisture-mechanic", false); - preventTrampling = mechanics.getBoolean("vanilla-farmland.prevent-trampling", false); - greenhouse = mechanics.getBoolean("greenhouse.enable", true); - greenhouseID = mechanics.getString("greenhouse.id"); - greenhouseRange = mechanics.getInt("greenhouse.range", 5); - glassItemType = ItemCarrier.valueOf(mechanics.getString("greenhouse.type", "CHORUS").toUpperCase(Locale.ENGLISH)); - - scarecrow = mechanics.getBoolean("scarecrow.enable", true); - scarecrowID = mechanics.getString("scarecrow.id"); - scarecrowRange = mechanics.getInt("scarecrow.range", 7); - scarecrowProtectChunk = mechanics.getBoolean("scarecrow.protect-chunk", false); - scarecrowItemType = ItemCarrier.valueOf(mechanics.getString("scarecrow.type", "ITEM_FRAME").toUpperCase(Locale.ENGLISH)); - - syncSeasons = mechanics.getBoolean("sync-season.enable", true); - if (syncSeasons) { - referenceWorld = new WeakReference<>(Bukkit.getWorld(mechanics.getString("sync-season.reference", "world"))); - } - } - - @Override - public void unload() { - - } - - @Override - public boolean hasLegacyColorSupport() { - return legacyColorSupport; - } - - @Override - public int getMaximumPoolSize() { - return maximumPoolSize; - } - - @Override - public int getCorePoolSize() { - return corePoolSize; - } - - @Override - public int getKeepAliveTime() { - return keepAliveTime; - } - - @Override - public boolean isConvertWorldOnLoad() { - return convertWorldOnLoad; - } - - @Override - public double[] getDefaultQualityRatio() { - return defaultQualityRatio; - } - - @Override - public String getLang() { - return lang; - } - - @Override - public boolean getDebugMode() { - return debug; - } - - @Override - public boolean isProtectLore() { - return protectLore; - } - - @Override - public String[] getItemDetectionOrder() { - return itemDetectionOrder; - } - - @Override - public boolean hasMetrics() { - return metrics; - } - - @Override - public boolean hasCheckUpdate() { - return checkUpdate; - } - - @Override - public boolean isDisableMoisture() { - return disableMoisture; - } - - @Override - public boolean isPreventTrampling() { - return preventTrampling; - } - - @Override - public boolean isGreenhouseEnabled() { - return greenhouse; - } - - @Override - public String getGreenhouseID() { - return greenhouseID; - } - - @Override - public int getGreenhouseRange() { - return greenhouseRange; - } - - @Override - public boolean isScarecrowEnabled() { - return scarecrow; - } - - @Override - public String getScarecrowID() { - return scarecrowID; - } - - @Override - public int getScarecrowRange() { - return scarecrowRange; - } - - @Override - public boolean isSyncSeasons() { - return syncSeasons; - } - - @Override - public boolean doesScarecrowProtectChunk() { - return scarecrowProtectChunk; - } - - @Override - public ItemCarrier getScarecrowItemCarrier() { - return scarecrowItemType; - } - - @Override - public ItemCarrier getGlassItemCarrier() { - return glassItemType; - } - - @Override - public World getReferenceWorld() { - return referenceWorld.get(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java deleted file mode 100644 index 154f77fd4..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.kyori.adventure.text.Component; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Reloadable; -import net.momirealms.customcrops.api.common.Tuple; -import net.momirealms.customcrops.api.manager.VersionManager; -import net.momirealms.customcrops.api.scheduler.CancellableTask; -import net.momirealms.customcrops.util.FakeEntityUtils; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerQuitEvent; - -import java.util.ArrayList; -import java.util.Map; -import java.util.UUID; -import java.util.Vector; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -public class HologramManager implements Listener, Reloadable { - - private final ConcurrentHashMap hologramMap; - private final CustomCropsPlugin plugin; - private CancellableTask cacheCheckTask; - private static HologramManager manager; - - public static HologramManager getInstance() { - return manager; - } - - public HologramManager(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.hologramMap = new ConcurrentHashMap<>(); - manager = this; - } - - @Override - public void load() { - Bukkit.getPluginManager().registerEvents(this, plugin); - this.cacheCheckTask = plugin.getScheduler().runTaskAsyncTimer(() -> { - ArrayList removed = new ArrayList<>(); - long current = System.currentTimeMillis(); - for (Map.Entry entry : hologramMap.entrySet()) { - Player player = Bukkit.getPlayer(entry.getKey()); - if (player == null || !player.isOnline()) { - removed.add(entry.getKey()); - } else { - entry.getValue().removeOutDated(current, player); - } - } - for (UUID uuid : removed) { - hologramMap.remove(uuid); - } - }, 200, 200, TimeUnit.MILLISECONDS); - } - - @Override - public void unload() { - HandlerList.unregisterAll(this); - for (Map.Entry entry : hologramMap.entrySet()) { - Player player = Bukkit.getPlayer(entry.getKey()); - if (player != null && player.isOnline()) { - entry.getValue().removeAll(player); - } - } - if (cacheCheckTask != null) cacheCheckTask.cancel(); - this.hologramMap.clear(); - } - - @EventHandler - public void onQuit(PlayerQuitEvent event) { - this.hologramMap.remove(event.getPlayer().getUniqueId()); - } - - public void showHologram(Player player, Location location, Component component, int millis) { - HologramCache hologramCache = hologramMap.get(player.getUniqueId()); - if (hologramCache != null) { - hologramCache.showHologram(player, location, component, millis); - } else { - hologramCache = new HologramCache(); - hologramCache.showHologram(player, location, component, millis); - hologramMap.put(player.getUniqueId(), hologramCache); - } - } - - @SuppressWarnings("unchecked") - public static class HologramCache { - - private final Vector> tupleList; - private Tuple[] tuples; - - public HologramCache() { - this.tupleList = new Vector<>(); - this.tuples = new Tuple[0]; - } - - public int push(Location new_loc, int time) { - for (Tuple tuple : tuples) { - if (new_loc.equals(tuple.getLeft())) { - tuple.setRight(System.currentTimeMillis() + time); - return tuple.getMid(); - } - } - return 0; - } - - public void removeOutDated(long current, Player player) { - for (Tuple tuple : tuples) { - if (tuple.getRight() < current) { - tupleList.remove(tuple); - this.tuples = tupleList.toArray(new Tuple[0]); - PacketManager.getInstance().send(player, FakeEntityUtils.getDestroyPacket(tuple.getMid())); - } - } - } - - public void showHologram(Player player, Location location, Component component, int millis) { - int entity_id = push(location, millis); - if (entity_id == 0) { - int random = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); - tupleList.add(Tuple.of(location, random, System.currentTimeMillis() + millis)); - this.tuples = tupleList.toArray(new Tuple[0]); - if (VersionManager.isHigherThan1_20_R2()) { - PacketManager.getInstance().send(player, - FakeEntityUtils.getSpawnPacket(random, location.clone().add(0,1.25,0), EntityType.TEXT_DISPLAY), - FakeEntityUtils.get1_20_2TextDisplayMetaPacket(random, component) - ); - } else if (VersionManager.isHigherThan1_19_R3()) { - PacketManager.getInstance().send(player, - FakeEntityUtils.getSpawnPacket(random, location.clone().add(0,1.25,0), EntityType.TEXT_DISPLAY), - FakeEntityUtils.get1_19_4TextDisplayMetaPacket(random, component) - ); - } else { - PacketManager.getInstance().send(player, - FakeEntityUtils.getSpawnPacket(random, location, EntityType.ARMOR_STAND), - FakeEntityUtils.getVanishArmorStandMetaPacket(random, component) - ); - } - } else { - if (VersionManager.isHigherThan1_20_R2()) { - PacketManager.getInstance().send(player, FakeEntityUtils.get1_20_2TextDisplayMetaPacket(entity_id, component)); - } else if (VersionManager.isHigherThan1_19_R3()) { - PacketManager.getInstance().send(player, FakeEntityUtils.get1_19_4TextDisplayMetaPacket(entity_id, component)); - } else { - PacketManager.getInstance().send(player, FakeEntityUtils.getVanishArmorStandMetaPacket(entity_id, component)); - } - } - } - - public void removeAll(Player player) { - for (Tuple tuple : tuples) { - PacketManager.getInstance().send(player, FakeEntityUtils.getDestroyPacket(tuple.getMid())); - } - this.tupleList.clear(); - this.tuples = null; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java deleted file mode 100644 index 5b90e30a1..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Reloadable; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.MessageManager; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; - -public class MessageManagerImpl extends MessageManager implements Reloadable { - - private CustomCropsPlugin plugin; - private String reload; - private String prefix; - private String spring; - private String summer; - private String autumn; - private String winter; - private String noSeason; - - public MessageManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - } - - @Override - public void load() { - YamlConfiguration config; - try { - config = ConfigUtils.getConfig("messages" + File.separator + ConfigManager.lang() + ".yml"); - } catch (Exception e) { - LogUtils.warn(ConfigManager.lang() + ".yml doesn't exist. Using the default language file now."); - config = ConfigUtils.getConfig("messages" + File.separator + "en" + ".yml"); - } - ConfigurationSection section = config.getConfigurationSection("messages"); - if (section != null) { - prefix = section.getString("prefix", "[CustomCrops] "); - reload = section.getString("reload", "Reloaded! Took {time}ms."); - - spring = section.getString("spring", "Spring"); - summer = section.getString("summer", "Summer"); - autumn = section.getString("autumn", "Autumn"); - winter = section.getString("winter", "Winter"); - noSeason = section.getString("no-season", "Season Disabled"); - } - } - - @Override - public void unload() { - - } - - @Override - public String getSeasonTranslation(Season season) { - if (season == null) return noSeason; - return switch (season) { - case SPRING -> spring; - case SUMMER -> summer; - case AUTUMN -> autumn; - case WINTER -> winter; - }; - } - - @Override - public void reload() { - load(); - } - - @Override - public String getPrefix() { - return prefix; - } - - @Override - protected String getReload() { - return reload; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/PacketManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/PacketManager.java deleted file mode 100644 index cc6f89273..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/PacketManager.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.ProtocolManager; -import com.comphenix.protocol.events.PacketContainer; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class PacketManager { - - private static PacketManager instance; - private final ProtocolManager protocolManager; - private final CustomCropsPlugin plugin; - - public PacketManager(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.protocolManager = ProtocolLibrary.getProtocolManager(); - instance = this; - } - - public static PacketManager getInstance() { - return instance; - } - - public void send(Player player, PacketContainer packet) { - this.protocolManager.sendServerPacket(player, packet); - } - - public void send(Player player, PacketContainer... packets) { - if (!player.isOnline()) return; - if (plugin.getVersionManager().isVersionNewerThan1_19_R3()) { - List bundle = new ArrayList<>(Arrays.asList(packets)); - PacketContainer bundlePacket = new PacketContainer(PacketType.Play.Server.BUNDLE); - bundlePacket.getPacketBundles().write(0, bundle); - send(player, bundlePacket); - } else { - for (PacketContainer packet : packets) { - send(player, packet); - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java deleted file mode 100644 index 0e36df4ce..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.manager; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.PlaceholderManager; -import net.momirealms.customcrops.compatibility.papi.CCPapi; -import net.momirealms.customcrops.compatibility.papi.ParseUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.stream.Collectors; - -public class PlaceholderManagerImpl extends PlaceholderManager { - - private final HashMap customPlaceholderMap; - private final CustomCropsPlugin plugin; - private final boolean hasPapi; - private CCPapi ccPapi; - - public PlaceholderManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.customPlaceholderMap = new HashMap<>(); - this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); - if (hasPapi) { - ccPapi = new CCPapi(plugin); - } - } - - @Override - public void load() { - if (ccPapi != null) { - ccPapi.register(); - } - this.loadCustomPlaceholders(); - } - - @Override - public void unload() { - if (ccPapi != null) { - ccPapi.unregister(); - } - this.customPlaceholderMap.clear(); - } - - public void loadCustomPlaceholders() { - YamlConfiguration config = ConfigUtils.getConfig("config.yml"); - ConfigurationSection section = config.getConfigurationSection("other-settings.placeholder-register"); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - this.customPlaceholderMap.put(entry.getKey(), (String) entry.getValue()); - } - } - } - - @Override - public String parse(@Nullable Player player, String text, Map placeholders) { - var list = detectPlaceholders(text); - for (String papi : list) { - String replacer = null; - if (placeholders != null) { - replacer = placeholders.get(papi); - } - if (replacer == null) { - String custom = customPlaceholderMap.get(papi); - if (custom != null) { - replacer = setPlaceholders(player, parse(player, custom, placeholders)); - } - } - if (replacer != null) { - text = text.replace(papi, replacer); - } - } - return text; - } - - @Override - public List parse(@Nullable Player player, List list, Map replacements) { - return list.stream() - .map(s -> parse(player, s, replacements)) - .collect(Collectors.toList()); - } - - @Override - public List detectPlaceholders(String text) { - List placeholders = new ArrayList<>(); - Matcher matcher = pattern.matcher(text); - while (matcher.find()) placeholders.add(matcher.group()); - return placeholders; - } - - @Override - public String setPlaceholders(Player player, String text) { - return hasPapi ? ParseUtils.setPlaceholders(player, text) : text; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java deleted file mode 100644 index 3aef0467c..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ /dev/null @@ -1,1206 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.action; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.text.Component; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.event.CropBreakEvent; -import net.momirealms.customcrops.api.event.CropPlantEvent; -import net.momirealms.customcrops.api.event.PotBreakEvent; -import net.momirealms.customcrops.api.event.SprinklerBreakEvent; -import net.momirealms.customcrops.api.manager.*; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionExpansion; -import net.momirealms.customcrops.api.mechanic.action.ActionFactory; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.*; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.QualityCrop; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.Variation; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.YieldIncrease; -import net.momirealms.customcrops.api.mechanic.misc.CRotation; -import net.momirealms.customcrops.api.mechanic.misc.Reason; -import net.momirealms.customcrops.api.mechanic.misc.Value; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; -import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; -import net.momirealms.customcrops.api.scheduler.CancellableTask; -import net.momirealms.customcrops.api.util.EventUtils; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.compatibility.VaultHook; -import net.momirealms.customcrops.manager.AdventureManagerImpl; -import net.momirealms.customcrops.manager.HologramManager; -import net.momirealms.customcrops.mechanic.item.impl.VariationCrop; -import net.momirealms.customcrops.mechanic.misc.TempFakeItem; -import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; -import net.momirealms.customcrops.util.ClassUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.customcrops.util.ItemUtils; -import net.momirealms.customcrops.util.ParticleUtils; -import net.momirealms.sparrow.heart.SparrowHeart; -import net.momirealms.sparrow.heart.feature.inventory.HandSlot; -import org.bukkit.*; -import org.bukkit.block.BlockFace; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.ExperienceOrb; -import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerItemDamageEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -public class ActionManagerImpl implements ActionManager { - - private final CustomCropsPlugin plugin; - private final HashMap actionBuilderMap; - private final String EXPANSION_FOLDER = "expansions/action"; - - public ActionManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.actionBuilderMap = new HashMap<>(); - this.registerInbuiltActions(); - } - - private void registerInbuiltActions() { - this.registerMessageAction(); - this.registerCommandAction(); - this.registerMendingAction(); - this.registerExpAction(); - this.registerChainAction(); - this.registerPotionAction(); - this.registerSoundAction(); - this.registerPluginExpAction(); - this.registerTitleAction(); - this.registerActionBarAction(); - this.registerCloseInvAction(); - this.registerDelayedAction(); - this.registerConditionalAction(); - this.registerPriorityAction(); - this.registerLevelAction(); - this.registerFakeItemAction(); - this.registerFoodAction(); - this.registerItemAmountAction(); - this.registerItemDurabilityAction(); - this.registerGiveItemAction(); - this.registerMoneyAction(); - this.registerTimerAction(); - this.registerParticleAction(); - this.registerSwingHandAction(); - this.registerDropItemsAction(); - this.registerBreakAction(); - this.registerPlantAction(); - this.registerQualityCropsAction(); - this.registerVariationAction(); - this.registerForceTickAction(); - this.registerHologramAction(); - this.registerLegacyDropItemsAction(); - } - - @Override - public void load() { - loadExpansions(); - } - - @Override - public void unload() { - - } - - @Override - public void disable() { - actionBuilderMap.clear(); - } - - @Override - public boolean registerAction(String type, ActionFactory actionFactory) { - if (this.actionBuilderMap.containsKey(type)) return false; - this.actionBuilderMap.put(type, actionFactory); - return true; - } - - @Override - public boolean unregisterAction(String type) { - return this.actionBuilderMap.remove(type) != null; - } - - @Override - public Action getAction(ConfigurationSection section) { - ActionFactory factory = getActionFactory(section.getString("type")); - if (factory == null) { - LogUtils.warn("Action type: " + section.getString("type") + " doesn't exist."); - // to prevent NPE - return EmptyAction.instance; - } - return factory.build( - section.get("value"), - section.getDouble("chance", 1d) - ); - } - - @Override - @NotNull - public HashMap getActionMap(ConfigurationSection section) { - HashMap actionMap = new HashMap<>(); - if (section == null) return actionMap; - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - try { - actionMap.put( - ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)), - getActions(innerSection) - ); - } catch (IllegalArgumentException e) { - LogUtils.warn("Event: " + entry.getKey() + " doesn't exist!"); - } - } - } - return actionMap; - } - - @NotNull - @Override - public Action[] getActions(ConfigurationSection section) { - ArrayList actionList = new ArrayList<>(); - if (section == null) return actionList.toArray(new Action[0]); - - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - Action action = getAction(innerSection); - if (action != null) - actionList.add(action); - } - } - return actionList.toArray(new Action[0]); - } - - @Nullable - @Override - public ActionFactory getActionFactory(String type) { - return actionBuilderMap.get(type); - } - - private void registerHologramAction() { - registerAction("hologram", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String text = section.getString("text", ""); - int duration = section.getInt("duration", 20); - double x = section.getDouble("x"); - double y = section.getDouble("y"); - double z = section.getDouble("z"); - boolean applyCorrection = section.getBoolean("apply-correction", false); - boolean onlyShowToOne = !section.getBoolean("visible-to-all", false); - return condition -> { - if (Math.random() > chance) return; - if (condition.getArg("{offline}") != null) return; - Location location = condition.getLocation().clone().add(x,y,z); - SimpleLocation simpleLocation = SimpleLocation.of(location); - if (applyCorrection) { - SimpleLocation cropLocation = simpleLocation.copy().add(0,1,0); - Optional crop = plugin.getWorldManager().getCropAt(cropLocation); - if (crop.isPresent()) { - WorldCrop worldCrop = crop.get(); - Crop config = worldCrop.getConfig(); - Crop.Stage stage = config.getStageByItemID(config.getStageItemByPoint(worldCrop.getPoint())); - if (stage != null) { - location.add(0, stage.getHologramOffset(), 0); - } - } - } - - ArrayList viewers = new ArrayList<>(); - if (onlyShowToOne) { - if (condition.getPlayer() == null) return; - viewers.add(condition.getPlayer()); - } else { - for (Player player : Bukkit.getOnlinePlayers()) { - if (simpleLocation.isNear(SimpleLocation.of(player.getLocation()), 48)) { - viewers.add(player); - } - } - } - Component component = AdventureManager.getInstance().getComponentFromMiniMessage(PlaceholderManager.getInstance().parse(condition.getPlayer(), text, condition.getArgs())); - for (Player viewer : viewers) { - HologramManager.getInstance().showHologram(viewer, location, component, duration * 50); - } - }; - } else { - LogUtils.warn("Illegal value format found at action: hologram"); - return EmptyAction.instance; - } - }); - } - - private void registerFakeItemAction() { - registerAction("fake-item", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String item = section.getString("item", ""); - int duration = section.getInt("duration", 20); - double x = section.getDouble("x"); - double y = section.getDouble("y"); - double z = section.getDouble("z"); - boolean onlyShowToOne = !section.getBoolean("visible-to-all", true); - return condition -> { - if (Math.random() > chance) return; - if (condition.getArg("{offline}") != null) return; - if (item.equals("")) return; - Location location = condition.getLocation().clone().add(x,y,z); - new TempFakeItem(location, item, duration, onlyShowToOne ? condition.getPlayer() : null).start(); - }; - } else { - LogUtils.warn("Illegal value format found at action: fake-item"); - return EmptyAction.instance; - } - }); - } - - private void registerMessageAction() { - registerAction("message", (args, chance) -> { - ArrayList msg = ConfigUtils.stringListArgs(args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - List replaced = PlaceholderManager.getInstance().parse( - state.getPlayer(), - msg, - state.getArgs() - ); - for (String text : replaced) { - AdventureManagerImpl.getInstance().sendPlayerMessage(state.getPlayer(), text); - } - }; - }); - registerAction("broadcast", (args, chance) -> { - ArrayList msg = ConfigUtils.stringListArgs(args); - return state -> { - if (Math.random() > chance) return; - List replaced = PlaceholderManager.getInstance().parse( - state.getPlayer(), - msg, - state.getArgs() - ); - for (Player player : Bukkit.getOnlinePlayers()) { - for (String text : replaced) { - AdventureManager.getInstance().sendPlayerMessage(player, text); - } - } - }; - }); - } - - private void registerCommandAction() { - registerAction("command", (args, chance) -> { - ArrayList cmd = ConfigUtils.stringListArgs(args); - return state -> { - if (Math.random() > chance) return; - List replaced = PlaceholderManager.getInstance().parse( - state.getPlayer(), - cmd, - state.getArgs() - ); - plugin.getScheduler().runTaskSync(() -> { - for (String text : replaced) { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); - } - }, state.getLocation()); - }; - }); - registerAction("random-command", (args, chance) -> { - ArrayList cmd = ConfigUtils.stringListArgs(args); - return state -> { - if (Math.random() > chance) return; - String random = cmd.get(ThreadLocalRandom.current().nextInt(cmd.size())); - random = PlaceholderManager.getInstance().parse(state.getPlayer(), random, state.getArgs()); - String finalRandom = random; - plugin.getScheduler().runTaskSync(() -> { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom); - }, state.getLocation()); - }; - }); - } - - private void registerCloseInvAction() { - registerAction("close-inv", (args, chance) -> state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - state.getPlayer().closeInventory(); - }); - } - - private void registerActionBarAction() { - registerAction("actionbar", (args, chance) -> { - String text = (String) args; - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - String parsed = PlaceholderManager.getInstance().parse(state.getPlayer(), text, state.getArgs()); - AdventureManagerImpl.getInstance().sendActionbar(state.getPlayer(), parsed); - }; - }); - registerAction("random-actionbar", (args, chance) -> { - ArrayList texts = ConfigUtils.stringListArgs(args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - String random = texts.get(ThreadLocalRandom.current().nextInt(texts.size())); - random = PlaceholderManager.getInstance().parse(state.getPlayer(), random, state.getArgs()); - AdventureManagerImpl.getInstance().sendActionbar(state.getPlayer(), random); - }; - }); - } - - private void registerMendingAction() { - registerAction("mending", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - if (CustomCropsPlugin.get().getVersionManager().isSpigot()) { - state.getPlayer().getLocation().getWorld().spawn(state.getPlayer().getLocation(), ExperienceOrb.class, e -> e.setExperience((int) value.get(state.getPlayer()))); - } else { - state.getPlayer().giveExp((int) value.get(state.getPlayer()), true); - AdventureManagerImpl.getInstance().sendSound(state.getPlayer(), Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1f, 1f); - } - }; - }); - } - - private void registerFoodAction() { - registerAction("food", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - Player player = state.getPlayer(); - player.setFoodLevel((int) (player.getFoodLevel() + value.get(player))); - }; - }); - registerAction("saturation", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - Player player = state.getPlayer(); - player.setSaturation((float) (player.getSaturation() + value.get(player))); - }; - }); - } - - private void registerExpAction() { - registerAction("exp", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - state.getPlayer().giveExp((int) value.get(state.getPlayer())); - AdventureManagerImpl.getInstance().sendSound(state.getPlayer(), Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1); - }; - }); - } - - private void registerSwingHandAction() { - registerAction("swing-hand", (args, chance) -> { - boolean arg = (boolean) args; - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - SparrowHeart.getInstance().swingHand(state.getPlayer(), arg ? HandSlot.MAIN : HandSlot.OFF); - }; - }); - } - - private void registerForceTickAction() { - registerAction("force-tick", (args, chance) -> state -> { - if (Math.random() > chance) return; - Location location = state.getLocation(); - plugin.getWorldManager().getCustomCropsWorld(location.getWorld()) - .flatMap(world -> world.getLoadedChunkAt(ChunkPos.getByBukkitChunk(location.getChunk()))) - .flatMap(chunk -> chunk.getBlockAt(SimpleLocation.of(location))) - .ifPresent(block -> { - block.tick(1, false); - if (block instanceof WorldSprinkler sprinkler) { - Sprinkler config = sprinkler.getConfig(); - state.setArg("{current}", String.valueOf(sprinkler.getWater())); - state.setArg("{water_bar}", config.getWaterBar() == null ? "" : config.getWaterBar().getWaterBar(sprinkler.getWater(), config.getStorage())); - } else if (block instanceof WorldPot pot) { - Pot config = pot.getConfig(); - state.setArg("{current}", String.valueOf(pot.getWater())); - state.setArg("{water_bar}", config.getWaterBar() == null ? "" : config.getWaterBar().getWaterBar(pot.getWater(), config.getStorage())); - state.setArg("{left_times}", String.valueOf(pot.getFertilizerTimes())); - state.setArg("{max_times}", String.valueOf(Optional.ofNullable(pot.getFertilizer()).map(Fertilizer::getTimes).orElse(0))); - state.setArg("{icon}", Optional.ofNullable(pot.getFertilizer()).map(Fertilizer::getIcon).orElse("")); - } - }); - }); - } - - private void registerVariationAction() { - registerAction("variation", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - boolean ignore = section.getBoolean("ignore-fertilizer", false); - ArrayList variationCrops = new ArrayList<>(); - for (String inner_key : section.getKeys(false)) { - if (inner_key.equals("ignore-fertilizer")) continue; - VariationCrop variationCrop = new VariationCrop( - section.getString(inner_key + ".item"), - ItemCarrier.valueOf(section.getString(inner_key + ".type", "TripWire").toUpperCase(Locale.ENGLISH)), - section.getDouble(inner_key + ".chance") - ); - variationCrops.add(variationCrop); - } - VariationCrop[] variations = variationCrops.toArray(new VariationCrop[0]); - return state -> { - if (Math.random() > chance) return; - double bonus = 0; - if (!ignore) { - Optional pot = plugin.getWorldManager().getPotAt(SimpleLocation.of(state.getLocation().clone().subtract(0,1,0))); - if (pot.isPresent()) { - Fertilizer fertilizer = pot.get().getFertilizer(); - if (fertilizer instanceof Variation variation) { - bonus += variation.getChanceBonus(); - } - } - } - for (VariationCrop variationCrop : variations) { - if (Math.random() < variationCrop.getChance() + bonus) { - SimpleLocation location = SimpleLocation.of(state.getLocation()); - plugin.getItemManager().removeAnythingAt(state.getLocation()); - plugin.getWorldManager().removeAnythingAt(location); - plugin.getItemManager().placeItem(state.getLocation(), variationCrop.getItemCarrier(), variationCrop.getItemID()); - Optional.ofNullable(plugin.getItemManager().getCropStageByStageID(variationCrop.getItemID())) - .ifPresent(stage -> plugin.getWorldManager().addCropAt(new MemoryCrop(location, stage.getCrop().getKey(), stage.getPoint()), location)); - break; - } - } - }; - } else { - LogUtils.warn("Illegal value format found at action: variation"); - return EmptyAction.instance; - } - }); - } - - private void registerQualityCropsAction() { - registerAction("quality-crops", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - Value min = ConfigUtils.getValue(section.get("min")); - Value max = ConfigUtils.getValue(section.get("max")); - boolean toInv = section.getBoolean("to-inventory", false); - String[] qualityLoots = new String[ConfigManager.defaultQualityRatio().length]; - for (int i = 1; i <= ConfigManager.defaultQualityRatio().length; i++) { - qualityLoots[i-1] = section.getString("items." + i); - if (qualityLoots[i-1] == null) { - LogUtils.warn("items." + i + " should not be null"); - qualityLoots[i-1] = ""; - } - } - return state -> { - if (Math.random() > chance) return; - double[] ratio = ConfigManager.defaultQualityRatio(); - int random = (int) ThreadLocalRandom.current().nextDouble(min.get(state.getPlayer()), max.get(state.getPlayer()) + 1); - Optional pot = plugin.getWorldManager().getPotAt(SimpleLocation.of(state.getLocation().clone().subtract(0,1,0))); - if (pot.isPresent()) { - Fertilizer fertilizer = pot.get().getFertilizer(); - if (fertilizer instanceof YieldIncrease yieldIncrease) { - random += yieldIncrease.getAmountBonus(); - } else if (fertilizer instanceof QualityCrop qualityCrop && Math.random() < qualityCrop.getChance()) { - ratio = qualityCrop.getRatio(); - } - } - for (int i = 0; i < random; i++) { - double r1 = Math.random(); - for (int j = 0; j < ratio.length; j++) { - if (r1 < ratio[j]) { - ItemStack drop = plugin.getItemManager().getItemStack(state.getPlayer(), qualityLoots[j]); - if (drop == null || drop.getType() == Material.AIR) return; - if (toInv && state.getPlayer() != null) { - ItemUtils.giveItem(state.getPlayer(), drop, 1); - } else { - state.getLocation().getWorld().dropItemNaturally(state.getLocation(), drop); - } - break; - } - } - } - }; - } else { - LogUtils.warn("Illegal value format found at action: quality-crops"); - return EmptyAction.instance; - } - }); - } - - private void registerDropItemsAction() { - registerAction("drop-item", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - boolean ignoreFertilizer = section.getBoolean("ignore-fertilizer", true); - String item = section.getString("item"); - Value min = ConfigUtils.getValue(section.get("min")); - Value max = ConfigUtils.getValue(section.get("max")); - boolean toInv = section.getBoolean("to-inventory", false); - return state -> { - if (Math.random() > chance) return; - ItemStack itemStack = plugin.getItemManager().getItemStack(state.getPlayer(), item); - if (itemStack != null) { - int random = (int) ThreadLocalRandom.current().nextDouble(min.get(state.getPlayer()), (max.get(state.getPlayer()) + 1)); - if (!ignoreFertilizer) { - Optional pot = plugin.getWorldManager().getPotAt(SimpleLocation.of(state.getLocation().clone().subtract(0,1,0))); - if (pot.isPresent()) { - Fertilizer fertilizer = pot.get().getFertilizer(); - if (fertilizer instanceof YieldIncrease yieldIncrease) { - random += yieldIncrease.getAmountBonus(); - } - } - } - itemStack.setAmount(random); - if (toInv && state.getPlayer() != null) { - ItemUtils.giveItem(state.getPlayer(), itemStack, random); - } else { - state.getLocation().getWorld().dropItemNaturally(state.getLocation(), itemStack); - } - } else { - LogUtils.warn("Item: " + item + " doesn't exist"); - } - }; - } else { - LogUtils.warn("Illegal value format found at action: drop-items"); - return EmptyAction.instance; - } - }); - } - - private void registerLegacyDropItemsAction() { - registerAction("drop-items", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - List actions = new ArrayList<>(); - ConfigurationSection otherItemSection = section.getConfigurationSection("other-items"); - if (otherItemSection != null) { - for (Map.Entry entry : otherItemSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - actions.add(getActionFactory("drop-item").build(inner, inner.getDouble("chance", 1))); - } - } - } - ConfigurationSection qualitySection = section.getConfigurationSection("quality-crops"); - if (qualitySection != null) { - actions.add(getActionFactory("quality-crops").build(qualitySection, 1)); - } - return state -> { - if (Math.random() > chance) return; - for (Action action : actions) { - action.trigger(state); - } - }; - } else { - LogUtils.warn("Illegal value format found at action: drop-items"); - return EmptyAction.instance; - } - }); - } - - private void registerPlantAction() { - for (String name : List.of("plant", "replant")) { - registerAction(name, (args, chance) -> { - if (args instanceof ConfigurationSection section) { - int point = section.getInt("point", 0); - String key = section.getString("crop"); - return state -> { - if (Math.random() > chance) return; - if (key == null) return; - Crop crop = plugin.getItemManager().getCropByID(key); - if (crop == null) { - LogUtils.warn("Crop: " + key + " doesn't exist."); - return; - } - Location location = state.getLocation(); - Pot pot = plugin.getItemManager().getPotByBlock(location.getBlock().getRelative(BlockFace.DOWN)); - if (pot == null) { - plugin.debug("Crop should be planted on a pot at " + location); - return; - } - // check whitelist - if (!crop.getPotWhitelist().contains(pot.getKey())) { - crop.trigger(ActionTrigger.WRONG_POT, state); - return; - } - // check plant requirements - if (!RequirementManager.isRequirementMet(state, crop.getPlantRequirements())) { - return; - } - // check limitation - if (plugin.getWorldManager().isReachLimit(SimpleLocation.of(location), ItemType.CROP)) { - crop.trigger(ActionTrigger.REACH_LIMIT, state); - return; - } - plugin.getScheduler().runTaskSync(() -> { - // fire event - if (state.getPlayer() != null) { - CropPlantEvent plantEvent = new CropPlantEvent(state.getPlayer(), state.getItemInHand(), location, crop, point); - if (EventUtils.fireAndCheckCancel(plantEvent)) { - return; - } - - plugin.getItemManager().placeItem(location, crop.getItemCarrier(), crop.getStageItemByPoint(plantEvent.getPoint()), crop.hasRotation() ? CRotation.RANDOM : CRotation.NONE); - plugin.getWorldManager().addCropAt(new MemoryCrop(SimpleLocation.of(location), crop.getKey(), plantEvent.getPoint()), SimpleLocation.of(location)); - } else { - plugin.getItemManager().placeItem(location, crop.getItemCarrier(), crop.getStageItemByPoint(point), crop.hasRotation() ? CRotation.RANDOM : CRotation.NONE); - plugin.getWorldManager().addCropAt(new MemoryCrop(SimpleLocation.of(location), crop.getKey(), point), SimpleLocation.of(location)); - } - }, state.getLocation()); - }; - } else { - LogUtils.warn("Illegal value format found at action: " + name); - return EmptyAction.instance; - } - }); - } - } - - private void registerBreakAction() { - registerAction("break", (args, chance) -> { - boolean arg = (boolean) (args == null ? true : args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) { - LogUtils.warn("Break action can only be triggered by players"); - return; - } - plugin.getScheduler().runTaskSync(() -> { - Optional removed = plugin.getWorldManager().getBlockAt(SimpleLocation.of(state.getLocation())); - if (removed.isPresent()) { - switch (removed.get().getType()) { - case SPRINKLER -> { - WorldSprinkler sprinkler = (WorldSprinkler) removed.get(); - SprinklerBreakEvent event = new SprinklerBreakEvent(state.getPlayer(), state.getLocation(), sprinkler, Reason.ACTION); - if (EventUtils.fireAndCheckCancel(event)) - return; - if (arg) sprinkler.getConfig().trigger(ActionTrigger.BREAK, state); - plugin.getItemManager().removeAnythingAt(state.getLocation()); - plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(state.getLocation())); - } - case CROP -> { - WorldCrop crop = (WorldCrop) removed.get(); - CropBreakEvent event = new CropBreakEvent(state.getPlayer(), state.getLocation(), crop, Reason.ACTION); - if (EventUtils.fireAndCheckCancel(event)) - return; - Crop cropConfig = crop.getConfig(); - if (arg) { - cropConfig.trigger(ActionTrigger.BREAK, state); - cropConfig.getStageByItemID(cropConfig.getStageItemByPoint(crop.getPoint())).trigger(ActionTrigger.BREAK, state); - } - plugin.getItemManager().removeAnythingAt(state.getLocation()); - plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(state.getLocation())); - } - case POT -> { - WorldPot pot = (WorldPot) removed.get(); - PotBreakEvent event = new PotBreakEvent(state.getPlayer(), state.getLocation(), pot, Reason.ACTION); - if (EventUtils.fireAndCheckCancel(event)) - return; - if (arg) pot.getConfig().trigger(ActionTrigger.BREAK, state); - plugin.getItemManager().removeAnythingAt(state.getLocation()); - plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(state.getLocation())); - } - } - } else { - plugin.getItemManager().removeAnythingAt(state.getLocation()); - } - }, state.getLocation()); - }; - }); - } - - private void registerParticleAction() { - registerAction("particle", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - Particle particleType = ParticleUtils.getParticle(section.getString("particle", "ASH").toUpperCase(Locale.ENGLISH)); - double x = section.getDouble("x",0); - double y = section.getDouble("y",0); - double z = section.getDouble("z",0); - double offSetX = section.getDouble("offset-x",0); - double offSetY = section.getDouble("offset-y",0); - double offSetZ = section.getDouble("offset-z",0); - int count = section.getInt("count", 1); - double extra = section.getDouble("extra", 0); - float scale = (float) section.getDouble("scale", 1d); - - ItemStack itemStack; - if (section.contains("itemStack")) - itemStack = CustomCropsPlugin.get() - .getItemManager() - .getItemStack(null, section.getString("itemStack")); - else - itemStack = null; - - Color color; - if (section.contains("color")) { - String[] rgb = section.getString("color","255,255,255").split(","); - color = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); - } else { - color = null; - } - - Color toColor; - if (section.contains("color")) { - String[] rgb = section.getString("to-color","255,255,255").split(","); - toColor = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); - } else { - toColor = null; - } - - return state -> { - if (Math.random() > chance) return; - state.getLocation().getWorld().spawnParticle( - particleType, - state.getLocation().getX() + x, - state.getLocation().getY() + y, - state.getLocation().getZ() + z, - count, - offSetX, - offSetY, - offSetZ, - extra, - itemStack != null ? - itemStack : - (color != null && toColor != null ? - new Particle.DustTransition(color, toColor, scale) : - (color != null ? - new Particle.DustOptions(color, scale) : - null - ) - ) - ); - }; - } else { - LogUtils.warn("Illegal value format found at action: particle"); - return EmptyAction.instance; - } - }); - } - - private void registerItemAmountAction() { - registerAction("item-amount", (args, chance) -> { - int amount = (int) args; - return state -> { - if (Math.random() > chance) return; - Player player = state.getPlayer(); - if (player == null) return; - ItemStack itemStack = player.getInventory().getItemInMainHand(); - itemStack.setAmount(Math.max(0, itemStack.getAmount() + amount)); - }; - }); - } - - private void registerItemDurabilityAction() { - registerAction("durability", (args, chance) -> { - int amount = (int) args; - return state -> { - if (Math.random() > chance) return; - ItemStack itemStack = state.getItemInHand(); - if (itemStack.getItemMeta() == null) - return; - if (amount > 0) { - ItemUtils.increaseDurability(itemStack, amount); - } else { - if (state.getPlayer().getGameMode() == GameMode.CREATIVE || itemStack.getItemMeta().isUnbreakable()) return; - - ItemMeta previousMeta = itemStack.getItemMeta().clone(); - PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(state.getPlayer(), itemStack, amount); - Bukkit.getPluginManager().callEvent(itemDamageEvent); - if (!itemStack.getItemMeta().equals(previousMeta) || itemDamageEvent.isCancelled()) { - return; - } - ItemUtils.decreaseDurability(state.getPlayer(), itemStack, -amount); - } - }; - }); - } - - private void registerGiveItemAction() { - registerAction("give-item", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String id = section.getString("item"); - int amount = section.getInt("amount", 1); - return state -> { - if (Math.random() > chance) return; - Player player = state.getPlayer(); - if (player == null) return; - ItemUtils.giveItem(player, Objects.requireNonNull(CustomCropsPlugin.get().getItemManager().getItemStack(player, id)), amount); - }; - } else { - LogUtils.warn("Illegal value format found at action: give-item"); - return EmptyAction.instance; - } - }); - } - - private void registerChainAction() { - registerAction("chain", (args, chance) -> { - List actions = new ArrayList<>(); - if (args instanceof ConfigurationSection section) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - actions.add(getAction(innerSection)); - } - } - } - return state -> { - if (Math.random() > chance) return; - for (Action action : actions) { - action.trigger(state); - } - }; - }); - } - - private void registerMoneyAction() { - registerAction("give-money", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - VaultHook.getEconomy().depositPlayer(state.getPlayer(), value.get(state.getPlayer())); - }; - }); - registerAction("take-money", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - VaultHook.getEconomy().withdrawPlayer(state.getPlayer(), value.get(state.getPlayer())); - }; - }); - } - - private void registerDelayedAction() { - registerAction("delay", (args, chance) -> { - List actions = new ArrayList<>(); - int delay; - boolean async; - if (args instanceof ConfigurationSection section) { - delay = section.getInt("delay", 1); - async = section.getBoolean("async", false); - ConfigurationSection actionSection = section.getConfigurationSection("actions"); - if (actionSection != null) { - for (Map.Entry entry : actionSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - actions.add(getAction(innerSection)); - } - } - } - } else { - delay = 1; - async = false; - } - return state -> { - if (Math.random() > chance) return; - if (async) { - plugin.getScheduler().runTaskSyncLater(() -> { - for (Action action : actions) { - action.trigger(state); - } - }, state.getLocation(), delay * 50L, TimeUnit.MILLISECONDS); - } else { - plugin.getScheduler().runTaskSyncLater(() -> { - for (Action action : actions) { - action.trigger(state); - } - }, state.getLocation(), delay * 50L, TimeUnit.MILLISECONDS); - } - }; - }); - } - - private void registerTimerAction() { - registerAction("timer", (args, chance) -> { - List actions = new ArrayList<>(); - int delay; - int duration; - int period; - boolean async; - if (args instanceof ConfigurationSection section) { - delay = section.getInt("delay", 2); - duration = section.getInt("duration", 20); - period = section.getInt("period", 2); - async = section.getBoolean("async", false); - ConfigurationSection actionSection = section.getConfigurationSection("actions"); - if (actionSection != null) { - for (Map.Entry entry : actionSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - actions.add(getAction(innerSection)); - } - } - } - } else { - delay = 1; - async = false; - duration = 20; - period = 1; - } - return state -> { - if (Math.random() > chance) return; - CancellableTask cancellableTask; - if (async) { - cancellableTask = plugin.getScheduler().runTaskAsyncTimer(() -> { - for (Action action : actions) { - action.trigger(state); - } - }, delay * 50L, period * 50L, TimeUnit.MILLISECONDS); - } else { - cancellableTask = plugin.getScheduler().runTaskSyncTimer(() -> { - for (Action action : actions) { - action.trigger(state); - } - }, state.getLocation(), delay, period); - } - plugin.getScheduler().runTaskSyncLater(cancellableTask::cancel, state.getLocation(), duration); - }; - }); - } - - private void registerTitleAction() { - registerAction("title", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String title = section.getString("title"); - String subtitle = section.getString("subtitle"); - int fadeIn = section.getInt("fade-in", 20); - int stay = section.getInt("stay", 30); - int fadeOut = section.getInt("fade-out", 10); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - AdventureManagerImpl.getInstance().sendTitle( - state.getPlayer(), - PlaceholderManager.getInstance().parse(state.getPlayer(), title, state.getArgs()), - PlaceholderManager.getInstance().parse(state.getPlayer(), subtitle, state.getArgs()), - fadeIn, - stay, - fadeOut - ); - }; - } else { - LogUtils.warn("Illegal value format found at action: title"); - return EmptyAction.instance; - } - }); - registerAction("random-title", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - List titles = section.getStringList("titles"); - if (titles.size() == 0) titles.add(""); - List subtitles = section.getStringList("subtitles"); - if (subtitles.size() == 0) subtitles.add(""); - int fadeIn = section.getInt("fade-in", 20); - int stay = section.getInt("stay", 30); - int fadeOut = section.getInt("fade-out", 10); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - AdventureManagerImpl.getInstance().sendTitle( - state.getPlayer(), - PlaceholderManager.getInstance().parse(state.getPlayer(), titles.get(ThreadLocalRandom.current().nextInt(titles.size())), state.getArgs()), - PlaceholderManager.getInstance().parse(state.getPlayer(), subtitles.get(ThreadLocalRandom.current().nextInt(subtitles.size())), state.getArgs()), - fadeIn, - stay, - fadeOut - ); - }; - } else { - LogUtils.warn("Illegal value format found at action: random-title"); - return EmptyAction.instance; - } - }); - } - - private void registerPotionAction() { - registerAction("potion-effect", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - PotionEffect potionEffect = new PotionEffect( - Objects.requireNonNull(PotionEffectType.getByName(section.getString("type", "BLINDNESS").toUpperCase(Locale.ENGLISH))), - section.getInt("duration", 20), - section.getInt("amplifier", 0) - ); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - state.getPlayer().addPotionEffect(potionEffect); - }; - } else { - LogUtils.warn("Illegal value format found at action: potion-effect"); - return EmptyAction.instance; - } - }); - } - - private void registerLevelAction() { - registerAction("level", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return state -> { - if (Math.random() > chance) return; - Player player = state.getPlayer(); - if (player == null) return; - player.setLevel((int) Math.max(0, player.getLevel() + value.get(state.getPlayer()))); - }; - }); - } - - @SuppressWarnings("all") - private void registerSoundAction() { - registerAction("sound", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - Sound sound = Sound.sound( - Key.key(section.getString("key")), - Sound.Source.valueOf(section.getString("source", "PLAYER").toUpperCase(Locale.ENGLISH)), - (float) section.getDouble("volume", 1), - (float) section.getDouble("pitch", 1) - ); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - AdventureManagerImpl.getInstance().sendSound(state.getPlayer(), sound); - }; - } else { - LogUtils.warn("Illegal value format found at action: sound"); - return EmptyAction.instance; - } - }); - } - - private void registerConditionalAction() { - registerAction("conditional", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - Action[] actions = getActions(section.getConfigurationSection("actions")); - Requirement[] requirements = plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions"), true); - return state -> { - if (Math.random() > chance) return; - if (requirements != null) - for (Requirement requirement : requirements) { - if (!requirement.isStateMet(state)) { - return; - } - } - for (Action action : actions) { - action.trigger(state); - } - }; - } else { - LogUtils.warn("Illegal value format found at action: conditional"); - return EmptyAction.instance; - } - }); - } - - private void registerPriorityAction() { - registerAction("priority", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - List> stateActionPairList = new ArrayList<>(); - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - Action[] actions = getActions(inner.getConfigurationSection("actions")); - Requirement[] requirements = plugin.getRequirementManager().getRequirements(inner.getConfigurationSection("states"), false); - stateActionPairList.add(Pair.of(requirements, actions)); - } - } - return state -> { - if (Math.random() > chance) return; - outer: - for (Pair pair : stateActionPairList) { - if (pair.left() != null) - for (Requirement requirement : pair.left()) { - if (!requirement.isStateMet(state)) { - continue outer; - } - } - if (pair.right() != null) - for (Action action : pair.right()) { - action.trigger(state); - } - return; - } - }; - } else { - LogUtils.warn("Illegal value format found at action: priority"); - return EmptyAction.instance; - } - }); - } - - private void registerPluginExpAction() { - registerAction("plugin-exp", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String pluginName = section.getString("plugin"); - var value = ConfigUtils.getValue(section.get("exp")); - String target = section.getString("target"); - return state -> { - if (Math.random() > chance) return; - if (state.getPlayer() == null) return; - Optional.ofNullable(plugin.getIntegrationManager().getLevelPlugin(pluginName)).ifPresentOrElse(it -> { - it.addXp(state.getPlayer(), target, value.get(state.getPlayer())); - }, () -> LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation.")); - }; - } else { - LogUtils.warn("Illegal value format found at action: plugin-exp"); - return EmptyAction.instance; - } - }); - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private void loadExpansions() { - File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); - if (!expansionFolder.exists()) - expansionFolder.mkdirs(); - - List> classes = new ArrayList<>(); - File[] expansionJars = expansionFolder.listFiles(); - if (expansionJars == null) return; - for (File expansionJar : expansionJars) { - if (expansionJar.getName().endsWith(".jar")) { - try { - Class expansionClass = ClassUtils.findClass(expansionJar, ActionExpansion.class); - classes.add(expansionClass); - } catch (IOException | ClassNotFoundException e) { - LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); - } - } - } - try { - for (Class expansionClass : classes) { - ActionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); - unregisterAction(expansion.getActionType()); - registerAction(expansion.getActionType(), expansion.getActionFactory()); - LogUtils.info("Loaded action expansion: " + expansion.getActionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); - } - } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { - LogUtils.warn("Error occurred when creating expansion instance.", e); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java deleted file mode 100644 index d57bd30a2..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java +++ /dev/null @@ -1,760 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.condition; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.manager.ConditionManager; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.mechanic.condition.Condition; -import net.momirealms.customcrops.api.mechanic.condition.ConditionExpansion; -import net.momirealms.customcrops.api.mechanic.condition.ConditionFactory; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.compatibility.papi.ParseUtils; -import net.momirealms.customcrops.mechanic.misc.CrowAttackAnimation; -import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; -import net.momirealms.customcrops.util.ClassUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.sparrow.heart.SparrowHeart; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.data.type.Farmland; -import org.bukkit.configuration.ConfigurationSection; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.*; - -public class ConditionManagerImpl implements ConditionManager { - - private final String EXPANSION_FOLDER = "expansions/condition"; - private final HashMap conditionBuilderMap; - private final CustomCropsPlugin plugin; - - public ConditionManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.conditionBuilderMap = new HashMap<>(); - this.registerInbuiltConditions(); - } - - @Override - public void load() { - this.loadExpansions(); - } - - @Override - public void unload() { - - } - - @Override - public void disable() { - this.conditionBuilderMap.clear(); - } - - private void registerInbuiltConditions() { - this.registerSeasonCondition(); - this.registerWaterCondition(); - this.registerTemperatureCondition(); - this.registerAndCondition(); - this.registerOrCondition(); - this.registerRandomCondition(); - this.registerEqualsCondition(); - this.registerNumberEqualCondition(); - this.registerRegexCondition(); - this.registerGreaterThanCondition(); - this.registerLessThanCondition(); - this.registerContainCondition(); - this.registerStartWithCondition(); - this.registerEndWithCondition(); - this.registerInListCondition(); - this.registerBiomeRequirement(); - this.registerFertilizerCondition(); - this.registerCrowAttackCondition(); - this.registerPotCondition(); - this.registerLightCondition(); - this.registerPointCondition(); - this.registerWorldRequirement(); - } - - @Override - public boolean registerCondition(String type, ConditionFactory conditionFactory) { - if (this.conditionBuilderMap.containsKey(type)) return false; - this.conditionBuilderMap.put(type, conditionFactory); - return true; - } - - @Override - public boolean unregisterCondition(String type) { - return this.conditionBuilderMap.remove(type) != null; - } - - @Override - public @NotNull Condition[] getConditions(ConfigurationSection section) { - ArrayList conditions = new ArrayList<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - String key = entry.getKey(); - if (hasCondition(key)) { - conditions.add(getCondition(key, innerSection)); - } else { - conditions.add(getCondition(section.getConfigurationSection(key))); - } - } - } - } - return conditions.toArray(new Condition[0]); - } - - @Override - public Condition getCondition(ConfigurationSection section) { - if (section == null) { - LogUtils.warn("Condition section should not be null"); - return EmptyCondition.instance; - } - return getCondition(section.getString("type"), section.get("value")); - } - - @Override - public Condition getCondition(String key, Object args) { - if (key == null) { - LogUtils.warn("Condition type should not be null"); - return EmptyCondition.instance; - } - ConditionFactory factory = getConditionFactory(key); - if (factory == null) { - LogUtils.warn("Condition type: " + key + " doesn't exist."); - return EmptyCondition.instance; - } - return factory.build(args); - } - - @Nullable - @Override - public ConditionFactory getConditionFactory(String type) { - return conditionBuilderMap.get(type); - } - - private void registerCrowAttackCondition() { - registerCondition("crow_attack", (args -> { - if (args instanceof ConfigurationSection section) { - String flyModel = section.getString("fly-model"); - String standModel = section.getString("stand-model"); - double chance = section.getDouble("chance"); - return (block, offline) -> { - if (Math.random() > chance) return false; - SimpleLocation location = block.getLocation(); - if (ConfigManager.enableScarecrow()) { - Optional world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName()); - if (world.isEmpty()) return false; - CustomCropsWorld customCropsWorld = world.get(); - if (!ConfigManager.scarecrowProtectChunk()) { - int range = ConfigManager.scarecrowRange(); - for (int i = -range; i <= range; i++) { - for (int j = -range; j <= range; j++) { - for (int k : new int[]{0,-1,1}) { - if (customCropsWorld.getScarecrowAt(location.copy().add(i, k, j)).isPresent()) { - return false; - } - } - } - } - } else { - if (customCropsWorld.doesChunkHaveScarecrow(location)) { - return false; - } - } - } - if (!offline) - new CrowAttackAnimation(location, flyModel, standModel).start(); - return true; - }; - } else { - LogUtils.warn("Wrong value format found at crow-attack condition."); - return EmptyCondition.instance; - } - })); - } - - private void registerWorldRequirement() { - registerCondition("world", (args) -> { - HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); - return (block, offline) -> worlds.contains(block.getLocation().getWorldName()); - }); - registerCondition("!world", (args) -> { - HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); - return (block, offline) -> !worlds.contains(block.getLocation().getWorldName()); - }); - } - - private void registerBiomeRequirement() { - registerCondition("biome", (args) -> { - HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return (block, offline) -> { - String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(block.getLocation().getBukkitLocation()); - return biomes.contains(currentBiome); - }; - }); - registerCondition("!biome", (args) -> { - HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return (block, offline) -> { - String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(block.getLocation().getBukkitLocation()); - return !biomes.contains(currentBiome); - }; - }); - } - - private void registerRandomCondition() { - registerCondition("random", (args -> { - double value = ConfigUtils.getDoubleValue(args); - return (block, offline) -> Math.random() < value; - })); - } - - private void registerPotCondition() { - registerCondition("pot", (args -> { - HashSet pots = new HashSet<>(ConfigUtils.stringListArgs(args)); - return (block, offline) -> { - Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); - return worldPot.filter(pot -> pots.contains(pot.getKey())).isPresent(); - }; - })); - registerCondition("!pot", (args -> { - HashSet pots = new HashSet<>(ConfigUtils.stringListArgs(args)); - return (block, offline) -> { - Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); - return worldPot.filter(pot -> !pots.contains(pot.getKey())).isPresent(); - }; - })); - } - - private void registerFertilizerCondition() { - registerCondition("fertilizer", (args -> { - HashSet fertilizer = new HashSet<>(ConfigUtils.stringListArgs(args)); - return (block, offline) -> { - Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); - return worldPot.filter(pot -> { - Fertilizer fertilizerInstance = pot.getFertilizer(); - if (fertilizerInstance == null) return false; - return fertilizer.contains(fertilizerInstance.getKey()); - }).isPresent(); - }; - })); - registerCondition("fertilizer_type", (args -> { - HashSet fertilizer = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(str -> str.toUpperCase(Locale.ENGLISH)).toList()); - return (block, offline) -> { - Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); - return worldPot.filter(pot -> { - Fertilizer fertilizerInstance = pot.getFertilizer(); - if (fertilizerInstance == null) return false; - return fertilizer.contains(fertilizerInstance.getFertilizerType().name()); - }).isPresent(); - }; - })); - } - - private void registerAndCondition() { - registerCondition("&&", (args -> { - if (args instanceof ConfigurationSection section) { - Condition[] conditions = getConditions(section); - return (block, offline) -> ConditionManager.isConditionMet(block, offline, conditions); - } else { - LogUtils.warn("Wrong value format found at && condition."); - return EmptyCondition.instance; - } - })); - } - - private void registerOrCondition() { - registerCondition("||", (args -> { - if (args instanceof ConfigurationSection section) { - Condition[] conditions = getConditions(section); - return (block, offline) -> { - for (Condition condition : conditions) { - if (condition.isConditionMet(block, offline)) { - return true; - } - } - return false; - }; - } else { - LogUtils.warn("Wrong value format found at || condition."); - return EmptyCondition.instance; - } - })); - } - - private void registerTemperatureCondition() { - registerCondition("temperature", (args) -> { - List> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return (block, offline) -> { - SimpleLocation location = block.getLocation(); - World world = location.getBukkitWorld(); - if (world == null) return false; - double temp = world.getTemperature(location.getX(), location.getY(), location.getZ()); - for (Pair pair : tempPairs) { - if (temp >= pair.left() && temp <= pair.right()) { - return true; - } - } - return false; - }; - }); - } - - @SuppressWarnings("DuplicatedCode") - private void registerGreaterThanCondition() { - registerCondition(">=", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return Double.parseDouble(p1) >= Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at >= requirement."); - return EmptyCondition.instance; - } - }); - registerCondition(">", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return Double.parseDouble(p1) > Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at > requirement."); - return EmptyCondition.instance; - } - }); - } - - private void registerRegexCondition() { - registerCondition("regex", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("papi", ""); - String v2 = section.getString("regex", ""); - return (block, offline) -> ParseUtils.setPlaceholders(null, v1).matches(v2); - } else { - LogUtils.warn("Wrong value format found at regex requirement."); - return EmptyCondition.instance; - } - }); - } - - private void registerNumberEqualCondition() { - registerCondition("==", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return Double.parseDouble(p1) == Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyCondition.instance; - } - }); - registerCondition("!=", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return Double.parseDouble(p1) != Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyCondition.instance; - } - }); - } - - @SuppressWarnings("DuplicatedCode") - private void registerLessThanCondition() { - registerCondition("<", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return Double.parseDouble(p1) < Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at < requirement."); - return EmptyCondition.instance; - } - }); - registerCondition("<=", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return Double.parseDouble(p1) <= Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at <= requirement."); - return EmptyCondition.instance; - } - }); - } - - private void registerStartWithCondition() { - registerCondition("startsWith", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return p1.startsWith(p2); - }; - } else { - LogUtils.warn("Wrong value format found at startsWith requirement."); - return EmptyCondition.instance; - } - }); - registerCondition("!startsWith", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return !p1.startsWith(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyCondition.instance; - } - }); - } - - private void registerEndWithCondition() { - registerCondition("endsWith", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return p1.endsWith(p2); - }; - } else { - LogUtils.warn("Wrong value format found at endsWith requirement."); - return EmptyCondition.instance; - } - }); - registerCondition("!endsWith", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return !p1.endsWith(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !endsWith requirement."); - return EmptyCondition.instance; - } - }); - } - - private void registerContainCondition() { - registerCondition("contains", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return p1.contains(p2); - }; - } else { - LogUtils.warn("Wrong value format found at contains requirement."); - return EmptyCondition.instance; - } - }); - registerCondition("!contains", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return !p1.contains(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !contains requirement."); - return EmptyCondition.instance; - } - }); - } - - private void registerInListCondition() { - registerCondition("in-list", (args) -> { - if (args instanceof ConfigurationSection section) { - String papi = section.getString("papi", ""); - HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); - return (block, offline) -> { - String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(null, papi) : papi; - return values.contains(p1); - }; - } else { - LogUtils.warn("Wrong value format found at in-list requirement."); - return EmptyCondition.instance; - } - }); - registerCondition("!in-list", (args) -> { - if (args instanceof ConfigurationSection section) { - String papi = section.getString("papi", ""); - HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); - return (block, offline) -> { - String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(null, papi) : papi; - return !values.contains(p1); - }; - } else { - LogUtils.warn("Wrong value format found at in-list requirement."); - return EmptyCondition.instance; - } - }); - } - - private void registerEqualsCondition() { - registerCondition("equals", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return p1.equals(p2); - }; - } else { - LogUtils.warn("Wrong value format found at equals requirement."); - return EmptyCondition.instance; - } - }); - registerCondition("!equals", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return (block, offline) -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; - return !p1.equals(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !equals requirement."); - return EmptyCondition.instance; - } - }); - } - - private void registerSeasonCondition() { - registerCondition("suitable_season", (args) -> { - HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); - return (block, offline) -> { - Season season = plugin.getIntegrationManager().getSeasonInterface().getSeason(block.getLocation().getBukkitWorld()); - if (season == null) { - return true; - } - if (seasons.contains(season.name())) { - return true; - } - if (ConfigManager.enableGreenhouse()) { - SimpleLocation location = block.getLocation(); - Optional world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName()); - if (world.isEmpty()) return false; - CustomCropsWorld customCropsWorld = world.get(); - for (int i = 1, range = ConfigManager.greenhouseRange(); i <= range; i++) { - if (customCropsWorld.getGlassAt(location.copy().add(0,i,0)).isPresent()) { - return true; - } - } - } - return false; - }; - }); - registerCondition("unsuitable_season", (args) -> { - HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); - return (block, offline) -> { - Season season = plugin.getIntegrationManager().getSeasonInterface().getSeason(block.getLocation().getBukkitWorld()); - if (season == null) { - return false; - } - if (seasons.contains(season.name())) { - if (ConfigManager.enableGreenhouse()) { - SimpleLocation location = block.getLocation(); - Optional world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName()); - if (world.isEmpty()) return false; - CustomCropsWorld customCropsWorld = world.get(); - for (int i = 1, range = ConfigManager.greenhouseRange(); i <= range; i++) { - if (customCropsWorld.getGlassAt(location.copy().add(0,i,0)).isPresent()) { - return false; - } - } - } - return true; - } - return false; - }; - }); - } - - private void registerLightCondition() { - registerCondition("skylight_more_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - int light = block.getLocation().getBukkitLocation().getBlock().getLightFromSky(); - return value > light; - }; - }); - registerCondition("skylight_less_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - int light = block.getLocation().getBukkitLocation().getBlock().getLightFromSky(); - return value < light; - }; - }); - registerCondition("light_more_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - int light = block.getLocation().getBukkitLocation().getBlock().getLightLevel(); - return value > light; - }; - }); - registerCondition("light_less_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - int light = block.getLocation().getBukkitLocation().getBlock().getLightLevel(); - return value < light; - }; - }); - } - - private void registerPointCondition() { - registerCondition("point_more_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - if (block instanceof MemoryCrop crop) { - return crop.getPoint() > value; - } - return false; - }; - }); - registerCondition("point_less_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - if (block instanceof MemoryCrop crop) { - return crop.getPoint() < value; - } - return false; - }; - }); - } - - private void registerWaterCondition() { - registerCondition("water_more_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); - return worldPot.filter(pot -> pot.getWater() > value).isPresent(); - }; - }); - registerCondition("water_less_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); - return worldPot.filter(pot -> pot.getWater() < value).isPresent(); - }; - }); - registerCondition("moisture_more_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - Block underBlock = block.getLocation().copy().add(0,-1,0).getBukkitLocation().getBlock(); - if (underBlock.getBlockData() instanceof Farmland farmland) { - return farmland.getMoisture() > value; - } - return false; - }; - }); - registerCondition("moisture_less_than", (args) -> { - int value = (int) args; - return (block, offline) -> { - Block underBlock = block.getLocation().copy().add(0,-1,0).getBukkitLocation().getBlock(); - if (underBlock.getBlockData() instanceof Farmland farmland) { - return farmland.getMoisture() < value; - } - return false; - }; - }); - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private void loadExpansions() { - File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); - if (!expansionFolder.exists()) - expansionFolder.mkdirs(); - - List> classes = new ArrayList<>(); - File[] expansionJars = expansionFolder.listFiles(); - if (expansionJars == null) return; - for (File expansionJar : expansionJars) { - if (expansionJar.getName().endsWith(".jar")) { - try { - Class expansionClass = ClassUtils.findClass(expansionJar, ConditionExpansion.class); - classes.add(expansionClass); - } catch (IOException | ClassNotFoundException e) { - LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); - } - } - } - try { - for (Class expansionClass : classes) { - ConditionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); - unregisterCondition(expansion.getConditionType()); - registerCondition(expansion.getConditionType(), expansion.getConditionFactory()); - LogUtils.info("Loaded condition expansion: " + expansion.getConditionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor()); - } - } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { - LogUtils.warn("Error occurred when creating expansion instance.", e); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/AbstractEventItem.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/AbstractEventItem.java deleted file mode 100644 index 92685f1b3..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/AbstractEventItem.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -import net.momirealms.customcrops.api.common.item.EventItem; -import net.momirealms.customcrops.api.manager.ActionManager; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.requirement.State; - -import java.util.HashMap; - -public abstract class AbstractEventItem implements EventItem { - - private final HashMap actionMap; - - public AbstractEventItem(HashMap actionMap) { - this.actionMap = actionMap; - } - - @Override - public void trigger(ActionTrigger actionTrigger, State state) { - Action[] actions = actionMap.get(actionTrigger); - if (actions != null) { - ActionManager.triggerActions(state, actions); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java deleted file mode 100644 index 035cd663c..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ /dev/null @@ -1,2766 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item; - -import com.google.common.base.Preconditions; -import net.momirealms.antigrieflib.AntiGriefLib; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.event.*; -import net.momirealms.customcrops.api.integration.ItemLibrary; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.ItemManager; -import net.momirealms.customcrops.api.manager.RequirementManager; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.condition.Conditions; -import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; -import net.momirealms.customcrops.api.mechanic.item.*; -import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; -import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; -import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; -import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; -import net.momirealms.customcrops.api.mechanic.misc.CRotation; -import net.momirealms.customcrops.api.mechanic.misc.Reason; -import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.*; -import net.momirealms.customcrops.api.util.EventUtils; -import net.momirealms.customcrops.api.util.LocationUtils; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderListener; -import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderProvider; -import net.momirealms.customcrops.mechanic.item.custom.oraxenlegacy.LegacyOraxenListener; -import net.momirealms.customcrops.mechanic.item.custom.oraxenlegacy.LegacyOraxenProvider; -import net.momirealms.customcrops.mechanic.item.function.CFunction; -import net.momirealms.customcrops.mechanic.item.function.FunctionResult; -import net.momirealms.customcrops.mechanic.item.function.FunctionTrigger; -import net.momirealms.customcrops.mechanic.item.function.wrapper.*; -import net.momirealms.customcrops.mechanic.item.impl.CropConfig; -import net.momirealms.customcrops.mechanic.item.impl.PotConfig; -import net.momirealms.customcrops.mechanic.item.impl.SprinklerConfig; -import net.momirealms.customcrops.mechanic.item.impl.WateringCanConfig; -import net.momirealms.customcrops.mechanic.item.impl.fertilizer.*; -import net.momirealms.customcrops.mechanic.world.block.*; -import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.customcrops.util.ItemUtils; -import net.momirealms.customcrops.util.RotationUtils; -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.block.data.type.Farmland; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.lang.reflect.Constructor; -import java.util.*; - -public class ItemManagerImpl implements ItemManager { - - private final CustomCropsPlugin plugin; - private AbstractCustomListener listener; - private CustomProvider customProvider; - private final HashMap itemLibraryMap; - private ItemLibrary[] itemDetectionArray; - private final HashMap>> itemID2FunctionMap; - private final HashMap id2WateringCanMap; - private final HashMap item2WateringCanMap; - private final HashMap id2SprinklerMap; - private final HashMap twoDItem2SprinklerMap; - private final HashMap threeDItem2SprinklerMap; - private final HashMap id2PotMap; - private final HashMap item2PotMap; - private final HashMap stage2CropMap; - private final HashMap seed2CropMap; - private final HashMap id2CropMap; - private final HashMap stage2CropStageMap; - private final HashMap id2FertilizerMap; - private final HashMap item2FertilizerMap; - private final AntiGriefLib antiGrief; - private final HashSet deadCrops; - - public ItemManagerImpl(CustomCropsPlugin plugin, AntiGriefLib antiGriefLib) { - this.plugin = plugin; - this.antiGrief = antiGriefLib; - this.itemLibraryMap = new HashMap<>(); - this.itemID2FunctionMap = new HashMap<>(); - this.id2WateringCanMap = new HashMap<>(); - this.item2WateringCanMap = new HashMap<>(); - this.id2SprinklerMap = new HashMap<>(); - this.twoDItem2SprinklerMap = new HashMap<>(); - this.threeDItem2SprinklerMap = new HashMap<>(); - this.id2PotMap = new HashMap<>(); - this.item2PotMap = new HashMap<>(); - this.stage2CropMap = new HashMap<>(); - this.seed2CropMap = new HashMap<>(); - this.id2CropMap = new HashMap<>(); - this.id2FertilizerMap = new HashMap<>(); - this.item2FertilizerMap = new HashMap<>(); - this.stage2CropStageMap = new HashMap<>(); - this.deadCrops = new HashSet<>(); - if (Bukkit.getPluginManager().getPlugin("Oraxen") != null) { - if (Bukkit.getPluginManager().getPlugin("Oraxen").getDescription().getVersion().startsWith("2")) { - try { - Class oraxenListenerClass = Class.forName("net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenListener"); - Constructor oraxenListenerConstructor = oraxenListenerClass.getDeclaredConstructor(ItemManager.class); - oraxenListenerConstructor.setAccessible(true); - this.listener = (AbstractCustomListener) oraxenListenerConstructor.newInstance(this); - Class oraxenProviderClass = Class.forName("net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenProvider"); - Constructor oraxenProviderConstructor = oraxenProviderClass.getDeclaredConstructor(); - oraxenProviderConstructor.setAccessible(true); - this.customProvider = (CustomProvider) oraxenProviderConstructor.newInstance(); - } catch (ReflectiveOperationException e) { - e.printStackTrace(); - } - } else { - listener = new LegacyOraxenListener(this); - customProvider = new LegacyOraxenProvider(); - } - } else if (Bukkit.getPluginManager().getPlugin("ItemsAdder") != null) { - listener = new ItemsAdderListener(this); - customProvider = new ItemsAdderProvider(); - } else if (Bukkit.getPluginManager().getPlugin("MythicCrucible") != null) { -// listener = new CrucibleListener(this); -// customProvider = new CrucibleProvider(); - } else { - LogUtils.severe("======================================================"); - LogUtils.severe(" Please install ItemsAdder or Oraxen as dependency."); - LogUtils.severe(" ItemsAdder: https://www.spigotmc.org/resources/73355/"); - LogUtils.severe(" Oraxen: https://www.spigotmc.org/resources/72448/"); - LogUtils.severe("======================================================"); - } - } - - @Override - public void load() { - this.loadItems(); - if (this.listener != null) { - Bukkit.getPluginManager().registerEvents(this.listener, this.plugin); - } - this.resetItemDetectionOrder(); - } - - @Override - public void unload() { - if (this.listener != null) { - HandlerList.unregisterAll(this.listener); - } - this.itemID2FunctionMap.clear(); - this.id2WateringCanMap.clear(); - this.item2WateringCanMap.clear(); - this.id2SprinklerMap.clear(); - this.twoDItem2SprinklerMap.clear(); - this.threeDItem2SprinklerMap.clear(); - this.id2PotMap.clear(); - this.item2PotMap.clear(); - this.stage2CropMap.clear(); - this.seed2CropMap.clear(); - this.id2CropMap.clear(); - this.stage2CropStageMap.clear(); - this.id2FertilizerMap.clear(); - this.item2FertilizerMap.clear(); - this.deadCrops.clear(); - CFunction.resetID(); - } - - @Override - public void disable() { - unload(); - this.itemLibraryMap.clear(); - } - - @Override - public boolean registerItemLibrary(@NotNull ItemLibrary itemLibrary) { - if (this.itemLibraryMap.containsKey(itemLibrary.identification())) - return false; - this.itemLibraryMap.put(itemLibrary.identification(), itemLibrary); - resetItemDetectionOrder(); - return true; - } - - @Override - public boolean unregisterItemLibrary(String identification) { - boolean success = this.itemLibraryMap.remove(identification) != null; - if (success) { - resetItemDetectionOrder(); - } - return success; - } - - private void resetItemDetectionOrder() { - ArrayList itemLibraries = new ArrayList<>(); - for (String identification : ConfigManager.itemDetectionOrder()) { - ItemLibrary library = itemLibraryMap.get(identification); - if (library != null) { - itemLibraries.add(library); - } - } - this.itemDetectionArray = itemLibraries.toArray(new ItemLibrary[0]); - } - - @Override - public String getItemID(ItemStack itemStack) { - if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) - return "AIR"; - String id; - id = customProvider.getItemID(itemStack); - if (id != null) return id; - else { - for (ItemLibrary library : itemDetectionArray) { - id = library.getItemID(itemStack); - if (id != null) - return library.identification() + ":" + id; - } - } - return itemStack.getType().name(); - } - - @Override - public ItemStack getItemStack(Player player, String id) { - ItemStack itemStack; - itemStack = customProvider.getItemStack(id); - if (itemStack != null) - return itemStack; - if (id.contains(":")) { - String[] split = id.split(":", 2); - ItemLibrary library = itemLibraryMap.get(split[0]); - if (library == null) { - LogUtils.warn("Error occurred when building item: " + id + ". Possible causes:"); - LogUtils.warn("① Item library: " + split[0] + " doesn't exist."); - LogUtils.warn("② If you are using ItemsAdder, " + id + " doesn't exist in your ItemsAdder config"); - return new ItemStack(Material.AIR); - } - return library.buildItem(player, split[1]); - } else { - try { - return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); - } catch (IllegalArgumentException e) { - return new ItemStack(Material.AIR); - } - } - } - - public boolean registerFertilizer(@NotNull Fertilizer fertilizer) { - if (this.id2FertilizerMap.containsKey(fertilizer.getKey())) { - return false; - } - this.id2FertilizerMap.put(fertilizer.getKey(), fertilizer); - if (this.item2FertilizerMap.put(fertilizer.getItemID(), fertilizer) != null) { - LogUtils.warn("Item " + fertilizer.getItemID() + " has more than one fertilizer config."); - return false; - } - return true; - } - - public boolean registerWateringCan(@NotNull WateringCan wateringCan) { - if (this.id2WateringCanMap.containsKey(wateringCan.getKey())) { - return false; - } - this.id2WateringCanMap.put(wateringCan.getKey(), wateringCan); - if (this.item2WateringCanMap.put(wateringCan.getItemID(), wateringCan) != null) { - LogUtils.warn("Item " + wateringCan.getItemID() + " has more than one watering-can config."); - return false; - } - return true; - } - - public boolean registerPot(@NotNull Pot pot) { - if (this.id2PotMap.containsKey(pot.getKey())) { - return false; - } - this.id2PotMap.put(pot.getKey(), pot); - for (String block : pot.getPotBlocks()) { - if (this.item2PotMap.put(block, pot) != null) { - LogUtils.warn("Block " + block + " has more than one pot config."); - return false; - } - } - return true; - } - - public boolean registerCrop(@NotNull Crop crop) { - if (this.id2CropMap.containsKey(crop.getKey())) { - return false; - } - this.id2CropMap.put(crop.getKey(), crop); - if (this.seed2CropMap.put(crop.getSeedItemID(), crop) != null) { - LogUtils.warn("Item " + crop.getSeedItemID() + " has more than one crop config."); - return false; - } - for (Crop.Stage stage : crop.getStages()) { - if (stage.getStageID() != null) { - if (this.stage2CropMap.put(stage.getStageID(), crop) != null) { - LogUtils.warn("Item " + stage.getStageID() + " has more than one crop config."); - return false; - } - if (this.stage2CropStageMap.put(stage.getStageID(), stage) != null) { - LogUtils.warn("Item " + stage.getStageID() + " has more than one crop config."); - return false; - } - } - } - return true; - } - - public boolean registerSprinkler(@NotNull Sprinkler sprinkler) { - if (this.id2SprinklerMap.containsKey(sprinkler.getKey())) { - return false; - } - this.id2SprinklerMap.put(sprinkler.getKey(), sprinkler); - if (sprinkler.get2DItemID() != null) { - if (this.twoDItem2SprinklerMap.put(sprinkler.get2DItemID(), sprinkler) != null) { - LogUtils.warn("Item " + sprinkler.get2DItemID() + " has more than one sprinkler config."); - return false; - } - } - if (this.threeDItem2SprinklerMap.put(sprinkler.get3DItemID(), sprinkler) != null) { - LogUtils.warn("Item " + sprinkler.get3DItemID() + " has more than one sprinkler config."); - return false; - } - if (sprinkler.get3DItemWithWater() != null) { - if (this.threeDItem2SprinklerMap.put(sprinkler.get3DItemWithWater(), sprinkler) != null) { - LogUtils.warn("Item " + sprinkler.get3DItemWithWater() + " has more than one sprinkler config."); - return false; - } - } - return true; - } - - @Override - public void placeItem(Location location, ItemCarrier carrier, String id) { - switch (carrier) { - case ITEM_DISPLAY, ITEM_FRAME -> { - customProvider.placeFurniture(location, id); - } - case TRIPWIRE, NOTE_BLOCK, CHORUS, MUSHROOM -> { - customProvider.placeBlock(location, id); - } - } - } - - @Override - public void placeItem(Location location, ItemCarrier carrier, String id, CRotation rotate) { - switch (carrier) { - case ITEM_DISPLAY, ITEM_FRAME -> { - Entity entity = customProvider.placeFurniture(location, id); - if (rotate == null || rotate == CRotation.NONE) return; - if (entity instanceof ItemFrame frame) { - frame.setRotation(RotationUtils.getBukkitRotation(rotate)); - } else if (entity instanceof ItemDisplay display) { - display.setRotation(RotationUtils.getFloatRotation(rotate), display.getLocation().getPitch()); - } - } - case TRIPWIRE, NOTE_BLOCK, CHORUS, MUSHROOM -> customProvider.placeBlock(location, id); - } - } - - @Override - public CRotation removeAnythingAt(Location location) { - return customProvider.removeAnythingAt(location); - } - - @Override - public CRotation getRotation(Location location) { - return customProvider.getRotation(location); - } - - @Override - public WateringCan getWateringCanByID(@NotNull String id) { - return id2WateringCanMap.get(id); - } - - @Override - public WateringCan getWateringCanByItemID(@NotNull String id) { - return item2WateringCanMap.get(id); - } - - @Override - public WateringCan getWateringCanByItemStack(@NotNull ItemStack itemStack) { - if (itemStack.getType() == Material.AIR) - return null; - return getWateringCanByItemID(getItemID(itemStack)); - } - - @Override - public Sprinkler getSprinklerByID(@NotNull String id) { - return id2SprinklerMap.get(id); - } - - @Override - public Sprinkler getSprinklerBy3DItemID(@NotNull String id) { - return threeDItem2SprinklerMap.get(id); - } - - @Override - public Sprinkler getSprinklerBy2DItemID(@NotNull String id) { - return twoDItem2SprinklerMap.get(id); - } - - @Override - public Sprinkler getSprinklerByEntity(@NotNull Entity entity) { - return Optional.ofNullable(customProvider.getEntityID(entity)).map(threeDItem2SprinklerMap::get).orElse(null); - } - - @Override - public Sprinkler getSprinklerByBlock(@NotNull Block block) { - return Optional.ofNullable(customProvider.getBlockID(block)).map(threeDItem2SprinklerMap::get).orElse(null); - } - - @Override - public Sprinkler getSprinklerBy2DItemStack(@NotNull ItemStack itemStack) { - return getSprinklerBy2DItemID(getItemID(itemStack)); - } - - @Override - public Sprinkler getSprinklerBy3DItemStack(@NotNull ItemStack itemStack) { - return getSprinklerBy3DItemID(getItemID(itemStack)); - } - - @Override - public Pot getPotByID(@NotNull String id) { - return id2PotMap.get(id); - } - - @Override - public Pot getPotByBlockID(@NotNull String id) { - return item2PotMap.get(id); - } - - @Override - public Pot getPotByBlock(@NotNull Block block) { - return getPotByBlockID(customProvider.getBlockID(block)); - } - - @Override - public Pot getPotByItemStack(@NotNull ItemStack itemStack) { - return getPotByBlockID(getItemID(itemStack)); - } - - @Override - public Fertilizer getFertilizerByID(@NotNull String id) { - return id2FertilizerMap.get(id); - } - - @Override - public Fertilizer getFertilizerByItemID(@NotNull String id) { - return item2FertilizerMap.get(id); - } - - @Override - public Fertilizer getFertilizerByItemStack(@NotNull ItemStack itemStack) { - if (itemStack.getType() == Material.AIR) - return null; - return item2FertilizerMap.get(getItemID(itemStack)); - } - - @Override - public Crop getCropByID(String id) { - return id2CropMap.get(id); - } - - @Override - public Crop getCropBySeedID(String id) { - return seed2CropMap.get(id); - } - - @Override - public Crop getCropBySeedItemStack(ItemStack itemStack) { - if (itemStack.getType() == Material.AIR) - return null; - return getCropByID(getItemID(itemStack)); - } - - @Override - public Crop getCropByStageID(String id) { - return stage2CropMap.get(id); - } - - @Override - public Crop getCropByEntity(Entity entity) { - return Optional.ofNullable(customProvider.getEntityID(entity)).map(stage2CropMap::get).orElse(null); - } - - @Override - public Crop getCropByBlock(Block block) { - return stage2CropMap.get(customProvider.getBlockID(block)); - } - - @Override - public Crop.Stage getCropStageByStageID(String id) { - return stage2CropStageMap.get(id); - } - - @SuppressWarnings("DuplicatedCode") - private void loadItems() { - for (String item : List.of("watering-cans", "pots", "crops", "sprinklers", "fertilizers")) { - File folder = new File(plugin.getDataFolder(), "contents" + File.separator + item); - if (!folder.exists()) { - plugin.saveResource("contents" + File.separator + item + File.separator + "default.yml", true); - ConfigUtils.addDefaultNamespace(new File(plugin.getDataFolder(), "contents" + File.separator + item + File.separator + "default.yml")); - } - List files = ConfigUtils.getFilesRecursively(new File(plugin.getDataFolder(), "contents" + File.separator + item)); - for (File file : files) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (Map.Entry entry : config.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection section) { - switch (item) { - case "watering-cans" -> loadWateringCan(entry.getKey(), section); - case "pots" -> loadPot(entry.getKey(), section); - case "crops" -> loadCrop(entry.getKey(), section); - case "sprinklers" -> loadSprinkler(entry.getKey(), section); - case "fertilizers" -> loadFertilizer(entry.getKey(), section); - } - } - } - } - } - this.loadDeadCrops(); - if (ConfigManager.enableGreenhouse()) - this.loadGreenhouse(); - if (ConfigManager.enableScarecrow()) - this.loadScarecrow(); - } - - private void loadDeadCrops() { - // register functions for dead crops - // or just keep it empty and let ItemsAdder/Oraxen handle the events - for (String id : deadCrops) { - this.registerItemFunction(id, FunctionTrigger.BE_INTERACTED, - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { - return FunctionResult.PASS; - } - Player player = furnitureWrapper.getPlayer(); - Location cropLocation = furnitureWrapper.getLocation(); - ItemStack itemInHand = furnitureWrapper.getItemInHand(); - String itemID = getItemID(itemInHand); - int itemAmount = itemInHand.getAmount(); - Location potLocation = cropLocation.clone().subtract(0,1,0); - Pot pot = getPotByBlock(potLocation.getBlock()); - State potState = new State(player, itemInHand, potLocation); - // check pot use requirements - if (pot != null && RequirementManager.isRequirementMet(potState, pot.getUseRequirements())) { - // get water in pot - int waterInPot = plugin.getWorldManager().getPotAt(SimpleLocation.of(potLocation)).map(WorldPot::getWater).orElse(0); - // water the pot - for (PassiveFillMethod method : pot.getPassiveFillMethods()) { - if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { - if (method.canFill(potState)) { - if (waterInPot < pot.getStorage()) { - if (player.getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemAmount - method.getUsedAmount()); - if (method.getReturned() != null) { - ItemStack returned = getItemStack(player, method.getReturned()); - ItemUtils.giveItem(player, returned, method.getReturnedAmount()); - } - } - method.trigger(potState); - pot.trigger(ActionTrigger.ADD_WATER, potState); - plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), SimpleLocation.of(potLocation)); - } else { - pot.trigger(ActionTrigger.FULL, potState); - } - } - return FunctionResult.RETURN; - } - } - } - return FunctionResult.PASS; - }, CFunction.FunctionPriority.NORMAL), - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { - return FunctionResult.PASS; - } - Player player = blockWrapper.getPlayer(); - Location cropLocation = blockWrapper.getLocation(); - ItemStack itemInHand = blockWrapper.getItemInHand(); - String itemID = getItemID(itemInHand); - int itemAmount = itemInHand.getAmount(); - Location potLocation = cropLocation.clone().subtract(0,1,0); - Pot pot = getPotByBlock(potLocation.getBlock()); - State potState = new State(player, itemInHand, potLocation); - // check pot use requirements - if (pot != null && RequirementManager.isRequirementMet(potState, pot.getUseRequirements())) { - // get water in pot - int waterInPot = plugin.getWorldManager().getPotAt(SimpleLocation.of(potLocation)).map(WorldPot::getWater).orElse(0); - // water the pot - for (PassiveFillMethod method : pot.getPassiveFillMethods()) { - if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { - if (method.canFill(potState)) { - if (waterInPot < pot.getStorage()) { - if (player.getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemAmount - method.getUsedAmount()); - if (method.getReturned() != null) { - ItemStack returned = getItemStack(player, method.getReturned()); - ItemUtils.giveItem(player, returned, method.getReturnedAmount()); - } - } - method.trigger(potState); - pot.trigger(ActionTrigger.ADD_WATER, potState); - plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), SimpleLocation.of(potLocation)); - } else { - pot.trigger(ActionTrigger.FULL, potState); - } - } - return FunctionResult.RETURN; - } - } - } - return FunctionResult.PASS; - }, CFunction.FunctionPriority.NORMAL) - ); - } - } - - private void loadGreenhouse() { - this.registerItemFunction(ConfigManager.greenhouseID(), FunctionTrigger.PLACE, - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof PlaceWrapper placeWrapper)) { - return FunctionResult.PASS; - } - // fire event - GreenhouseGlassPlaceEvent event = new GreenhouseGlassPlaceEvent(placeWrapper.getPlayer(), placeWrapper.getLocation()); - if (EventUtils.fireAndCheckCancel(event)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - SimpleLocation simpleLocation = SimpleLocation.of(placeWrapper.getLocation()); - plugin.getWorldManager().addGlassAt(new MemoryGlass(simpleLocation), simpleLocation); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - this.registerItemFunction(ConfigManager.greenhouseID(), FunctionTrigger.BREAK, - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof BreakWrapper breakWrapper)) { - return FunctionResult.PASS; - } - - // get or fix - Location location = breakWrapper.getLocation(); - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional optionalWorldGlass = plugin.getWorldManager().getGlassAt(simpleLocation); - if (optionalWorldGlass.isEmpty()) { - WorldGlass glass = new MemoryGlass(simpleLocation); - optionalWorldGlass = Optional.of(glass); - plugin.getWorldManager().addGlassAt(glass, simpleLocation); - } - - // fire event - GreenhouseGlassBreakEvent event = new GreenhouseGlassBreakEvent(breakWrapper.getPlayer(), location, optionalWorldGlass.get(), Reason.BREAK); - if (EventUtils.fireAndCheckCancel(event)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - plugin.getWorldManager().removeGlassAt(simpleLocation); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - } - - private void loadScarecrow() { - this.registerItemFunction(ConfigManager.scarecrowID(), FunctionTrigger.PLACE, - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof PlaceWrapper placeWrapper)) { - return FunctionResult.PASS; - } - // fire event - ScarecrowPlaceEvent event = new ScarecrowPlaceEvent(placeWrapper.getPlayer(), placeWrapper.getLocation()); - if (EventUtils.fireAndCheckCancel(event)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - SimpleLocation simpleLocation = SimpleLocation.of(placeWrapper.getLocation()); - plugin.getWorldManager().addScarecrowAt(new MemoryScarecrow(simpleLocation), simpleLocation); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - this.registerItemFunction(ConfigManager.scarecrowID(), FunctionTrigger.BREAK, - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof BreakWrapper breakWrapper)) { - return FunctionResult.PASS; - } - - // get or fix - Location location = breakWrapper.getLocation(); - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional optionalWorldScarecrow = plugin.getWorldManager().getScarecrowAt(simpleLocation); - if (optionalWorldScarecrow.isEmpty()) { - WorldScarecrow scarecrow = new MemoryScarecrow(simpleLocation); - optionalWorldScarecrow = Optional.of(scarecrow); - plugin.getWorldManager().addScarecrowAt(scarecrow, simpleLocation); - } - - // fire event - ScarecrowBreakEvent event = new ScarecrowBreakEvent(breakWrapper.getPlayer(), location, optionalWorldScarecrow.get(), Reason.BREAK); - if (EventUtils.fireAndCheckCancel(event)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - plugin.getWorldManager().removeScarecrowAt(simpleLocation); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - } - - @SuppressWarnings("DuplicatedCode") - private void loadWateringCan(String key, ConfigurationSection section) { - String itemID = section.getString("item"); - int width = section.getInt("effective-range.width"); - int length = section.getInt("effective-range.length"); - HashSet potWhiteList = new HashSet<>(section.getStringList("pot-whitelist")); - HashSet sprinklerWhiteList = new HashSet<>(section.getStringList("sprinkler-whitelist")); - boolean hasDynamicLore = section.getBoolean("dynamic-lore.enable", false); - List lore = section.getStringList("dynamic-lore.lore"); - - WateringCanConfig wateringCan = new WateringCanConfig( - key, - itemID, section.getBoolean("infinite", false), width, - length, section.getInt("capacity", 3), section.getInt("water", 1), - hasDynamicLore, lore, - potWhiteList, sprinklerWhiteList, - ConfigUtils.getPositiveFillMethods(section.getConfigurationSection("fill-method")), - ConfigUtils.getInt2IntMap(section.getConfigurationSection("appearance")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), - ConfigUtils.getActionMap(section.getConfigurationSection("events")), - section.contains("water-bar") ? WaterBar.of( - section.getString("water-bar.left", ""), - section.getString("water-bar.empty", ""), - section.getString("water-bar.full", ""), - section.getString("water-bar.right", "") - ) : null - ); - - if (!this.registerWateringCan(wateringCan)) { - LogUtils.warn("Failed to register new watering can: " + key + " due to duplicated entries."); - return; - } - - this.registerItemFunction(itemID, FunctionTrigger.INTERACT_AT, - /* - * Handle clicking sprinkler with a watering can - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { - return FunctionResult.PASS; - } - // is a pot - Block block = blockWrapper.getClickedBlock(); - Sprinkler sprinkler = getSprinklerBy3DItemID(customProvider.getBlockID(block)); - if (sprinkler == null) { - return FunctionResult.PASS; - } - final Player player = blockWrapper.getPlayer(); - final ItemStack itemInHand = blockWrapper.getItemInHand(); - final Location clicked = block.getLocation(); - State state = new State(player, itemInHand, clicked); - // check watering-can requirements - if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { - return FunctionResult.RETURN; - } - // check whitelist - if (!wateringCan.getSprinklerWhitelist().contains(sprinkler.getKey())) { - wateringCan.trigger(ActionTrigger.WRONG_SPRINKLER, state); - return FunctionResult.RETURN; - } - // get water in can - int waterInCan = wateringCan.getCurrentWater(itemInHand); - - // check sprinkler requirements - if (!RequirementManager.isRequirementMet(state, sprinkler.getUseRequirements())) { - return FunctionResult.RETURN; - } - // check whitelist - if (!wateringCan.getSprinklerWhitelist().contains(sprinkler.getKey())) { - wateringCan.trigger(ActionTrigger.WRONG_SPRINKLER, state); - return FunctionResult.RETURN; - } - // check amount of water - if (waterInCan > 0 || wateringCan.isInfinite()) { - // get sprinkler data - SimpleLocation simpleLocation = SimpleLocation.of(clicked); - Optional worldSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation); - if (worldSprinkler.isEmpty()) { - plugin.debug("Player " + player.getName() + " tried to interact a sprinkler which not exists in memory. Fixing the data..."); - WorldSprinkler sp = new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0); - plugin.getWorldManager().addSprinklerAt(sp, simpleLocation); - worldSprinkler = Optional.of(sp); - } else { - if (sprinkler.getStorage() <= worldSprinkler.get().getWater()) { - return FunctionResult.RETURN; - } - } - - // fire the event - WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemInHand, new HashSet<>(Set.of(clicked)), wateringCan, worldSprinkler.get()); - if (EventUtils.fireAndCheckCancel(waterEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", String.valueOf(waterInCan - 1)); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); - wateringCan.updateItem(player, itemInHand, waterInCan - 1, state.getArgs()); - wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); - plugin.getWorldManager().addWaterToSprinkler(sprinkler, simpleLocation, 1); - } else { - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", "0"); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); - wateringCan.trigger(ActionTrigger.NO_WATER, state); - } - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.HIGH), - /* - * Handle clicking pot with a watering can - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { - return FunctionResult.PASS; - } - // click the upper face - if (blockWrapper.getClickedFace() != BlockFace.UP) { - return FunctionResult.PASS; - } - // is a pot - Block block = blockWrapper.getClickedBlock(); - Pot pot = getPotByBlock(block); - if (pot == null) { - return FunctionResult.PASS; - } - final Player player = blockWrapper.getPlayer(); - final ItemStack itemStack = blockWrapper.getItemInHand(); - final Location clicked = block.getLocation(); - State state = new State(player, itemStack, clicked); - // check watering-can requirements - if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { - return FunctionResult.RETURN; - } - // check whitelist - if (!wateringCan.getPotWhitelist().contains(pot.getKey())) { - wateringCan.trigger(ActionTrigger.WRONG_POT, state); - return FunctionResult.RETURN; - } - // check amount of water - int waterInCan = wateringCan.getCurrentWater(itemStack); - - if (waterInCan > 0 || wateringCan.isInfinite()) { - - Collection pots = getPotInRange(clicked, wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey()); - // get or fix pot - SimpleLocation simpleLocation = SimpleLocation.of(clicked); - Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); - if (worldPot.isEmpty()) { - plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it."); - MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey()); - plugin.getWorldManager().addPotAt(memoryPot, simpleLocation); - worldPot = Optional.of(memoryPot); - } - - // fire the event - WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, new HashSet<>(pots), wateringCan, worldPot.get()); - if (EventUtils.fireAndCheckCancel(waterEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", String.valueOf(waterInCan - 1)); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); - wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs()); - wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); - - for (Location location : waterEvent.getLocation()) { - plugin.getWorldManager().addWaterToPot(pot, wateringCan.getWater(), SimpleLocation.of(location)); - pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); - } - } else { - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", "0"); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); - wateringCan.trigger(ActionTrigger.NO_WATER, state); - } - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.HIGH), - /* - * Handle clicking crop with a watering can - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { - return FunctionResult.PASS; - } - // is a crop - String id = customProvider.getBlockID(blockWrapper.getClickedBlock()); - Crop crop = getCropByStageID(id); - if (crop == null && !deadCrops.contains(id)) { - return FunctionResult.PASS; - } - // get pot block - Block potBlock = blockWrapper.getClickedBlock().getRelative(BlockFace.DOWN); - Pot pot = getPotByBlock(potBlock); - if (pot == null) { - plugin.debug("Unexpected issue: Detetced that crops are not planted on a pot: " + blockWrapper.getClickedBlock().getLocation()); - return FunctionResult.RETURN; - } - - final Player player = blockWrapper.getPlayer(); - final ItemStack itemStack = blockWrapper.getItemInHand(); - final Location clicked = blockWrapper.getClickedBlock().getLocation(); - State state = new State(player, itemStack, clicked); - // check watering-can use requirements - if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { - return FunctionResult.RETURN; - } - // check crop interact requirements - if (crop != null && !RequirementManager.isRequirementMet(state, crop.getInteractRequirements())) { - return FunctionResult.RETURN; - } - // check pot use requirements - if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { - return FunctionResult.RETURN; - } - // check watering-can whitelist - if (!wateringCan.getPotWhitelist().contains(pot.getKey())) { - wateringCan.trigger(ActionTrigger.WRONG_POT, state); - return FunctionResult.RETURN; - } - // check amount of water - int waterInCan = wateringCan.getCurrentWater(itemStack); - - if (waterInCan > 0 || wateringCan.isInfinite()) { - - Collection pots = getPotInRange(potBlock.getLocation(), wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey()); - - // get or fix pot - SimpleLocation simpleLocation = SimpleLocation.of(potBlock.getLocation()); - Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); - if (worldPot.isEmpty()) { - plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it."); - MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey()); - plugin.getWorldManager().addPotAt(memoryPot, simpleLocation); - worldPot = Optional.of(memoryPot); - } - - // fire the event - WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, new HashSet<>(pots), wateringCan, worldPot.get()); - if (EventUtils.fireAndCheckCancel(waterEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", String.valueOf(waterInCan - 1)); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); - wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs()); - wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); - - for (Location location : waterEvent.getLocation()) { - plugin.getWorldManager().addWaterToPot(pot, wateringCan.getWater(), SimpleLocation.of(location)); - pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); - } - } else { - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", "0"); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); - wateringCan.trigger(ActionTrigger.NO_WATER, state); - } - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL), - /* - * Handle clicking crop with a watering can - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { - return FunctionResult.PASS; - } - // is a crop - String id = furnitureWrapper.getID(); - Crop crop = getCropByStageID(id); - if (crop == null && !deadCrops.contains(id)) { - return FunctionResult.PASS; - } - // get pot block - Block potBlock = furnitureWrapper.getLocation().getBlock().getRelative(BlockFace.DOWN); - Pot pot = getPotByBlock(potBlock); - if (pot == null) { - LogUtils.warn("Unexpected issue: Detetced that crops are not planted on a pot: " + furnitureWrapper.getLocation()); - return FunctionResult.RETURN; - } - - final Player player = furnitureWrapper.getPlayer(); - final ItemStack itemStack = furnitureWrapper.getItemInHand(); - final Location clicked = furnitureWrapper.getLocation(); - State state = new State(player, itemStack, clicked); - // check watering-can use requirements - if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { - return FunctionResult.RETURN; - } - // check crop interact requirements - if (crop != null && !RequirementManager.isRequirementMet(state, crop.getInteractRequirements())) { - return FunctionResult.RETURN; - } - // check pot use requirements - if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { - return FunctionResult.RETURN; - } - // check watering-can whitelist - if (!wateringCan.getPotWhitelist().contains(pot.getKey())) { - wateringCan.trigger(ActionTrigger.WRONG_POT, state); - return FunctionResult.RETURN; - } - // check amount of water - int waterInCan = wateringCan.getCurrentWater(itemStack); - - if (waterInCan > 0 || wateringCan.isInfinite()) { - - Collection pots = getPotInRange(potBlock.getLocation(), wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey()); - - // get or fix pot - SimpleLocation simpleLocation = SimpleLocation.of(potBlock.getLocation()); - Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); - if (worldPot.isEmpty()) { - plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it."); - MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey()); - plugin.getWorldManager().addPotAt(memoryPot, simpleLocation); - worldPot = Optional.of(memoryPot); - } - - // fire the event - WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, new HashSet<>(pots), wateringCan, worldPot.get()); - if (EventUtils.fireAndCheckCancel(waterEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", String.valueOf(waterInCan - 1)); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); - wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs()); - wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); - - for (Location location : waterEvent.getLocation()) { - plugin.getWorldManager().addWaterToPot(pot, wateringCan.getWater(), SimpleLocation.of(location)); - pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); - } - } else { - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", "0"); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); - wateringCan.trigger(ActionTrigger.NO_WATER, state); - } - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL), - /* - * Handle clicking furniture with a watering can - * This furniture may be a sprinkler, or it may be a custom piece of furniture such as a well - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { - return FunctionResult.PASS; - } - // check watering-can requirements - Player player = furnitureWrapper.getPlayer(); - ItemStack itemInHand = furnitureWrapper.getItemInHand(); - Location location = furnitureWrapper.getLocation(); - State state = new State(player, itemInHand, location); - if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { - return FunctionResult.RETURN; - } - // get water in can - int waterInCan = wateringCan.getCurrentWater(itemInHand); - String clickedFurnitureID = furnitureWrapper.getID(); - Sprinkler sprinkler = getSprinklerBy3DItemID(clickedFurnitureID); - // is a sprinkler - if (sprinkler != null) { - // check sprinkler requirements - if (!RequirementManager.isRequirementMet(state, sprinkler.getUseRequirements())) { - return FunctionResult.RETURN; - } - // check whitelist - if (!wateringCan.getSprinklerWhitelist().contains(sprinkler.getKey())) { - wateringCan.trigger(ActionTrigger.WRONG_SPRINKLER, state); - return FunctionResult.RETURN; - } - // check amount of water - if (waterInCan > 0 || wateringCan.isInfinite()) { - // get sprinkler data - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional worldSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation); - if (worldSprinkler.isEmpty()) { - plugin.debug("Player " + player.getName() + " tried to interact a sprinkler which not exists in memory. Fixing the data..."); - WorldSprinkler sp = new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0); - plugin.getWorldManager().addSprinklerAt(sp, simpleLocation); - worldSprinkler = Optional.of(sp); - } else { - if (sprinkler.getStorage() <= worldSprinkler.get().getWater()) { - return FunctionResult.RETURN; - } - } - - // fire the event - WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemInHand, new HashSet<>(Set.of(location)), wateringCan, worldSprinkler.get()); - if (EventUtils.fireAndCheckCancel(waterEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", String.valueOf(waterInCan - 1)); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); - wateringCan.updateItem(player, furnitureWrapper.getItemInHand(), waterInCan - 1, state.getArgs()); - wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); - plugin.getWorldManager().addWaterToSprinkler(sprinkler, simpleLocation, 1); - } else { - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", "0"); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); - wateringCan.trigger(ActionTrigger.NO_WATER, state); - } - return FunctionResult.RETURN; - } - - // get water from furniture and add it to watering-can - if (!wateringCan.isInfinite()) { - PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); - for (PositiveFillMethod method : methods) { - if (method.getID().equals(clickedFurnitureID)) { - if (method.canFill(state)) { - // fire the event - WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, location, wateringCan, method); - if (EventUtils.fireAndCheckCancel(fillEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - if (waterInCan < wateringCan.getStorage()) { - waterInCan += method.getAmount(); - waterInCan = Math.min(waterInCan, wateringCan.getStorage()); - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", String.valueOf(waterInCan)); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan, wateringCan.getStorage())); - wateringCan.updateItem(player, furnitureWrapper.getItemInHand(), waterInCan, state.getArgs()); - wateringCan.trigger(ActionTrigger.ADD_WATER, state); - method.trigger(state); - } else { - wateringCan.trigger(ActionTrigger.FULL, state); - } - } - return FunctionResult.RETURN; - } - } - } - - return FunctionResult.PASS; - }, CFunction.FunctionPriority.NORMAL), - /* - * Handle clicking a block or air with watering can - * The priority of handling this is the lowest, - * because the priority of watering is much higher than that of adding water, - * and there should be no further judgment on adding water when watering occurs. - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { - return FunctionResult.PASS; - } - if (wateringCan.isInfinite()) { - return FunctionResult.PASS; - } - Player player = interactWrapper.getPlayer(); - ItemStack itemInHand = interactWrapper.getItemInHand(); - // get the clicked block - Block targetBlock = player.getTargetBlockExact(5, FluidCollisionMode.ALWAYS); - if (targetBlock == null) - return FunctionResult.PASS; - // check watering-can requirements - State state = new State(player, itemInHand, targetBlock.getLocation()); - if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { - return FunctionResult.RETURN; - } - // get the exact block id - String blockID = customProvider.getBlockID(targetBlock); - if (targetBlock.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { - blockID = "WATER"; - } - int water = wateringCan.getCurrentWater(itemInHand); - PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); - for (PositiveFillMethod method : methods) { - if (method.getID().equals(blockID)) { - if (method.canFill(state)) { - if (water < wateringCan.getStorage()) { - // fire the event - WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, state.getLocation(), wateringCan, method); - if (EventUtils.fireAndCheckCancel(fillEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - water += method.getAmount(); - water = Math.min(water, wateringCan.getStorage()); - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", String.valueOf(water)); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(water, wateringCan.getStorage())); - wateringCan.updateItem(player, itemInHand, water, state.getArgs()); - wateringCan.trigger(ActionTrigger.ADD_WATER, state); - method.trigger(state); - } else { - wateringCan.trigger(ActionTrigger.FULL, state); - } - } - return FunctionResult.RETURN; - } - } - return FunctionResult.PASS; - }, CFunction.FunctionPriority.LOW) - ); - - this.registerItemFunction(itemID, FunctionTrigger.INTERACT_AIR, - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { - return FunctionResult.PASS; - } - if (wateringCan.isInfinite()) { - return FunctionResult.PASS; - } - Player player = interactWrapper.getPlayer(); - // get the clicked block - Block targetBlock = player.getTargetBlockExact(5, FluidCollisionMode.ALWAYS); - if (targetBlock == null) - return FunctionResult.PASS; - // check watering-can requirements - ItemStack itemInHand = interactWrapper.getItemInHand(); - State state = new State(player, itemInHand, targetBlock.getLocation()); - if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { - return FunctionResult.RETURN; - } - // get the exact block id - String blockID = customProvider.getBlockID(targetBlock); - if (targetBlock.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { - blockID = "WATER"; - } - int water = wateringCan.getCurrentWater(itemInHand); - PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); - for (PositiveFillMethod method : methods) { - if (method.getID().equals(blockID)) { - if (method.canFill(state)) { - if (water < wateringCan.getStorage()) { - // fire the event - WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, state.getLocation(), wateringCan, method); - if (EventUtils.fireAndCheckCancel(fillEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - water += method.getAmount(); - water = Math.min(water, wateringCan.getStorage()); - state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); - state.setArg("{current}", String.valueOf(water)); - state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(water, wateringCan.getStorage())); - wateringCan.updateItem(player, itemInHand, water, state.getArgs()); - wateringCan.trigger(ActionTrigger.ADD_WATER, state); - method.trigger(state); - } else { - wateringCan.trigger(ActionTrigger.FULL, state); - } - } - return FunctionResult.RETURN; - } - } - return FunctionResult.PASS; - }, CFunction.FunctionPriority.NORMAL) - ); - } - - @SuppressWarnings("DuplicatedCode") - private void loadSprinkler(String key, ConfigurationSection section) { - int storage = section.getInt("storage", 4); - boolean infinite = section.getBoolean("infinite", false); - int range = section.getInt("range",1); - int water = section.getInt("water", 1); - ItemCarrier itemCarrier = ItemCarrier.valueOf(section.getString("type", "ITEM_FRAME").toUpperCase(Locale.ENGLISH)); - - SprinklerConfig sprinkler = new SprinklerConfig( - key, - itemCarrier, - section.getString("2D-item"), - Preconditions.checkNotNull(section.getString("3D-item"), "3D-item can't be null"), - section.getString("3D-item-with-water"), - range, - storage, - water, - infinite, - section.contains("water-bar") ? WaterBar.of( - section.getString("water-bar.left", ""), - section.getString("water-bar.empty", ""), - section.getString("water-bar.full", ""), - section.getString("water-bar.right", "") - ) : null, - new HashSet<>(section.getStringList("pot-whitelist")), - ConfigUtils.getPassiveFillMethods(section.getConfigurationSection("fill-method")), - ConfigUtils.getActionMap(section.getConfigurationSection("events")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.place")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.break")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.use")) - ); - - if (!this.registerSprinkler(sprinkler)) { - LogUtils.warn("Failed to register new sprinkler: " + key + " due to duplicated entries."); - return; - } - - if (sprinkler.get2DItemID() != null) { - this.registerItemFunction(sprinkler.get2DItemID(), FunctionTrigger.INTERACT_AT, - /* - * 2D item -> 3D item - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { - return FunctionResult.PASS; - } - if (interactBlockWrapper.getClickedFace() != BlockFace.UP) { - return FunctionResult.PASS; - } - if (!interactBlockWrapper.getClickedBlock().getType().isSolid()) { - return FunctionResult.PASS; - } - ItemStack itemInHand = interactBlockWrapper.getItemInHand(); - Location placed = interactBlockWrapper.getClickedBlock().getLocation().clone().add(0,1,0); - Player player = interactBlockWrapper.getPlayer(); - // check if the place is empty - if (!customProvider.isAir(placed)) { - return FunctionResult.RETURN; - } - // check place requirements - State state = new State(player, itemInHand, placed); - if (!RequirementManager.isRequirementMet(state, sprinkler.getPlaceRequirements())) { - return FunctionResult.RETURN; - } - // check limitation - SimpleLocation simpleLocation = SimpleLocation.of(placed); - if (plugin.getWorldManager().isReachLimit(simpleLocation, ItemType.SPRINKLER)) { - sprinkler.trigger(ActionTrigger.REACH_LIMIT, state); - return FunctionResult.RETURN; - } - // fire event - SprinklerPlaceEvent placeEvent = new SprinklerPlaceEvent(player, itemInHand, placed, sprinkler); - if (EventUtils.fireAndCheckCancel(placeEvent)) { - return FunctionResult.RETURN; - } - // place the sprinkler - switch (sprinkler.getItemCarrier()) { - case ITEM_FRAME, ITEM_DISPLAY -> customProvider.placeFurniture(placed, sprinkler.get3DItemID()); - case TRIPWIRE -> customProvider.placeBlock(placed, sprinkler.get3DItemID()); - default -> { - LogUtils.warn("Unsupported type for sprinkler: " + sprinkler.getItemCarrier().name()); - return FunctionResult.RETURN; - } - } - // reduce item - if (player.getGameMode() != GameMode.CREATIVE) - itemInHand.setAmount(itemInHand.getAmount() - 1); - sprinkler.trigger(ActionTrigger.PLACE, state); - plugin.getWorldManager().addSprinklerAt(new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0), simpleLocation); - return FunctionResult.PASS; - }, CFunction.FunctionPriority.NORMAL) - ); - } - - this.registerItemFunction(new String[]{sprinkler.get3DItemID(), sprinkler.get3DItemWithWater()}, FunctionTrigger.PLACE, - /* - * This will only trigger if the sprinkler has only 3D items - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof PlaceFurnitureWrapper placeFurnitureWrapper)) { - return FunctionResult.PASS; - } - Location location = placeFurnitureWrapper.getLocation(); - Player player = placeFurnitureWrapper.getPlayer(); - // check place requirements - State state = new State(player, placeFurnitureWrapper.getItemInHand(), location); - if (!RequirementManager.isRequirementMet(state, sprinkler.getPlaceRequirements())) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // check limitation - SimpleLocation simpleLocation = SimpleLocation.of(location); - if (plugin.getWorldManager().isReachLimit(simpleLocation, ItemType.SPRINKLER)) { - sprinkler.trigger(ActionTrigger.REACH_LIMIT, state); - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // fire event - SprinklerPlaceEvent placeEvent = new SprinklerPlaceEvent(player, placeFurnitureWrapper.getItemInHand(), location, sprinkler); - if (EventUtils.fireAndCheckCancel(placeEvent)) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // add data - plugin.getWorldManager().addSprinklerAt(new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0), simpleLocation); - sprinkler.trigger(ActionTrigger.PLACE, state); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - - this.registerItemFunction(new String[]{sprinkler.get3DItemID(), sprinkler.get3DItemWithWater()}, FunctionTrigger.BE_INTERACTED, - /* - * Interact the sprinkler - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { - return FunctionResult.PASS; - } - ItemStack itemInHand = interactWrapper.getItemInHand(); - Player player = interactWrapper.getPlayer(); - Location location = interactWrapper.getLocation(); - // check use requirements - State state = new State(player, itemInHand, location); - if (!RequirementManager.isRequirementMet(state, sprinkler.getUseRequirements())) { - return FunctionResult.RETURN; - } - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional optionalSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation); - if (optionalSprinkler.isEmpty()) { - plugin.debug("Found a sprinkler without data interacted by " + player.getName() + " at " + location); - WorldSprinkler newSprinkler = new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0); - plugin.getWorldManager().addSprinklerAt(newSprinkler, simpleLocation); - optionalSprinkler = Optional.of(newSprinkler); - } else { - if (!optionalSprinkler.get().getKey().equals(sprinkler.getKey())) { - LogUtils.warn("Found a sprinkler having inconsistent data interacted by " + player.getName() + " at " + location + "."); - plugin.getWorldManager().addSprinklerAt(new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0), simpleLocation); - return FunctionResult.RETURN; - } - } - - // fire the event - SprinklerInteractEvent interactEvent = new SprinklerInteractEvent(player, itemInHand, location, optionalSprinkler.get()); - if (EventUtils.fireAndCheckCancel(interactEvent)) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // add water to sprinkler - String itemID = getItemID(itemInHand); - int itemAmount = itemInHand.getAmount(); - int waterInSprinkler = optionalSprinkler.get().getWater(); - // if it's not infinite - if (!sprinkler.isInfinite()) { - for (PassiveFillMethod method : sprinkler.getPassiveFillMethods()) { - if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { - if (method.canFill(state)) { - if (waterInSprinkler < sprinkler.getStorage()) { - // fire the event - SprinklerFillEvent fillEvent = new SprinklerFillEvent(player, itemInHand, location, method, optionalSprinkler.get()); - if (EventUtils.fireAndCheckCancel(fillEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - if (player.getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemAmount - method.getUsedAmount()); - if (method.getReturned() != null) { - ItemStack returned = getItemStack(player, method.getReturned()); - ItemUtils.giveItem(player, returned, method.getReturnedAmount()); - } - } - int current = Math.min(waterInSprinkler + method.getAmount(), sprinkler.getStorage()); - state.setArg("{storage}", String.valueOf(sprinkler.getStorage())); - state.setArg("{current}", String.valueOf(current)); - state.setArg("{water_bar}", sprinkler.getWaterBar() == null ? "" : sprinkler.getWaterBar().getWaterBar(current, sprinkler.getStorage())); - method.trigger(state); - sprinkler.trigger(ActionTrigger.ADD_WATER, state); - plugin.getWorldManager().addWaterToSprinkler(sprinkler, simpleLocation, method.getAmount()); - } else { - state.setArg("{storage}", String.valueOf(sprinkler.getStorage())); - state.setArg("{current}", String.valueOf(sprinkler.getStorage())); - state.setArg("{water_bar}", sprinkler.getWaterBar() == null ? "" : sprinkler.getWaterBar().getWaterBar(sprinkler.getStorage(), sprinkler.getStorage())); - sprinkler.trigger(ActionTrigger.FULL, state); - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - } - return FunctionResult.RETURN; - } - } - } - - return FunctionResult.PASS; - }, CFunction.FunctionPriority.NORMAL) - ); - - this.registerItemFunction(new String[]{sprinkler.get3DItemID(), sprinkler.get3DItemWithWater()}, FunctionTrigger.BE_INTERACTED, - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { - return FunctionResult.PASS; - } - - Location location = interactWrapper.getLocation(); - // trigger interact actions - plugin.getScheduler().runTaskSyncLater(() -> { - State state = new State(interactWrapper.getPlayer(), interactWrapper.getItemInHand(), location); - Optional optionalSprinkler = plugin.getWorldManager().getSprinklerAt(SimpleLocation.of(location)); - if (optionalSprinkler.isEmpty()) { - return; - } - - state.setArg("{storage}", String.valueOf(sprinkler.getStorage())); - state.setArg("{current}", String.valueOf(optionalSprinkler.get().getWater())); - state.setArg("{water_bar}", sprinkler.getWaterBar() == null ? "" : sprinkler.getWaterBar().getWaterBar(optionalSprinkler.get().getWater(), sprinkler.getStorage())); - - sprinkler.trigger(ActionTrigger.INTERACT, state); - }, location, 1); - - return FunctionResult.PASS; - }, CFunction.FunctionPriority.LOWEST) - ); - - this.registerItemFunction(new String[]{sprinkler.get3DItemID(), sprinkler.get3DItemWithWater()}, FunctionTrigger.BREAK, - /* - * Handle breaking sprinklers - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof BreakFurnitureWrapper breakFurnitureWrapper)) { - return FunctionResult.PASS; - } - // check break requirements - Location location = breakFurnitureWrapper.getLocation(); - State state = new State(breakFurnitureWrapper.getPlayer(), breakFurnitureWrapper.getItemInHand(), location); - if (!RequirementManager.isRequirementMet(state, sprinkler.getBreakRequirements())) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional optionalSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation); - if (optionalSprinkler.isEmpty()) { - plugin.debug("Found a sprinkler without data broken by " + state.getPlayer().getName() + " at " + location); - return FunctionResult.RETURN; - } - if (!optionalSprinkler.get().getKey().equals(sprinkler.getKey())) { - LogUtils.warn("Found a sprinkler having inconsistent data broken by " + state.getPlayer().getName() + " at " + location + "."); - plugin.getWorldManager().removeSprinklerAt(simpleLocation); - return FunctionResult.RETURN; - } - // fire event - SprinklerBreakEvent breakEvent = new SprinklerBreakEvent(breakFurnitureWrapper.getPlayer(), location, optionalSprinkler.get(), Reason.BREAK); - if (EventUtils.fireAndCheckCancel(breakEvent)) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // remove data - plugin.getWorldManager().removeSprinklerAt(simpleLocation); - sprinkler.trigger(ActionTrigger.BREAK, state); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - } - - @SuppressWarnings("DuplicatedCode") - private void loadFertilizer(String key, ConfigurationSection section) { - FertilizerType type = switch (Preconditions.checkNotNull(section.getString("type"), "Fertilizer type can't be null").toUpperCase(Locale.ENGLISH)) { - case "QUALITY" -> FertilizerType.QUALITY; - case "SOIL_RETAIN" -> FertilizerType.SOIL_RETAIN; - case "SPEED_GROW" -> FertilizerType.SPEED_GROW; - case "VARIATION" -> FertilizerType.VARIATION; - case "YIELD_INCREASE" -> FertilizerType.YIELD_INCREASE; - default -> null; - }; - - if (type == null) { - LogUtils.warn("Fertilizer type: " + section.getString("type") + " is invalid."); - return; - } - - String icon = section.getString("icon", ""); - int times = section.getInt("times", 14); - String itemID = section.getString("item"); - HashSet potWhitelist = new HashSet<>(section.getStringList("pot-whitelist")); - boolean beforePlant = section.getBoolean("before-plant", false); - - Fertilizer fertilizer; - switch (type) { - case QUALITY -> fertilizer = new QualityCropConfig( - key, itemID, times, - section.getDouble("chance", 1), type, potWhitelist, - beforePlant, icon, - ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), - ConfigUtils.getQualityRatio(Preconditions.checkNotNull(section.getString("ratio"), "Quality ratio should not be null")), - ConfigUtils.getActionMap(section.getConfigurationSection("events")) - ); - case VARIATION -> fertilizer = new VariationConfig(key, itemID, times, - section.getDouble("chance", 1), type, potWhitelist, - beforePlant, icon, - ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), - ConfigUtils.getActionMap(section.getConfigurationSection("events")) - ); - case SOIL_RETAIN -> fertilizer = new SoilRetainConfig(key, itemID, times, - section.getDouble("chance", 1), type, potWhitelist, - beforePlant, icon, - ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), - ConfigUtils.getActionMap(section.getConfigurationSection("events")) - ); - case YIELD_INCREASE -> fertilizer = new YieldIncreaseConfig(key, itemID, times, - type, potWhitelist, - beforePlant, icon, - ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), - ConfigUtils.getIntChancePair(section.getConfigurationSection("chance")), - ConfigUtils.getActionMap(section.getConfigurationSection("events")) - ); - case SPEED_GROW -> fertilizer = new SpeedGrowConfig(key, itemID, times, - type, potWhitelist, - beforePlant, icon, - ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), - ConfigUtils.getIntChancePair(section.getConfigurationSection("chance")), - ConfigUtils.getActionMap(section.getConfigurationSection("events")) - ); - default -> fertilizer = null; - } - - if (!registerFertilizer(fertilizer)) { - LogUtils.warn("Failed to register new fertilizer: " + key + " due to duplicated entries."); - return; - } - - this.registerItemFunction(fertilizer.getItemID(), FunctionTrigger.INTERACT_AT, - /* - * Processing logic for players to use fertilizer - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { - return FunctionResult.PASS; - } - // is a pot - Block clicked = interactBlockWrapper.getClickedBlock(); - Pot pot = getPotByBlock(clicked); - if (pot == null) { - return FunctionResult.PASS; - } - ItemStack itemInHand = interactBlockWrapper.getItemInHand(); - Location location = clicked.getLocation(); - // check fertilizer requirements - State state = new State(interactBlockWrapper.getPlayer(), itemInHand, location); - if (!RequirementManager.isRequirementMet(state, fertilizer.getRequirements())) { - return FunctionResult.RETURN; - } - // check pot use requirements - if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { - return FunctionResult.RETURN; - } - // check whitelist - if (!fertilizer.getPotWhitelist().contains(pot.getKey())) { - fertilizer.trigger(ActionTrigger.WRONG_POT, state); - return FunctionResult.RETURN; - } - // check before plant - if (fertilizer.isBeforePlant()) { - Optional worldCrop = plugin.getWorldManager().getCropAt(SimpleLocation.of(location.clone().add(0,1,0))); - if (worldCrop.isPresent()) { - fertilizer.trigger(ActionTrigger.BEFORE_PLANT, state); - return FunctionResult.RETURN; - } - } - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); - boolean hasWater = false; - if (worldPot.isEmpty()) { - plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it."); - MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey()); - plugin.getWorldManager().addPotAt(memoryPot, simpleLocation); - worldPot = Optional.of(memoryPot); - } else { - hasWater = worldPot.get().getWater() > 0; - } - // fire the event - FertilizerUseEvent useEvent = new FertilizerUseEvent(state.getPlayer(), itemInHand, fertilizer, location, worldPot.get()); - if (EventUtils.fireAndCheckCancel(useEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - // add data - plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); - if (interactBlockWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemInHand.getAmount() - 1); - } - fertilizer.trigger(ActionTrigger.USE, state); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL), - - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { - return FunctionResult.PASS; - } - // is a crop - Block clicked = interactBlockWrapper.getClickedBlock(); - String id = customProvider.getBlockID(clicked); - Crop crop = getCropByStageID(id); - if (crop == null && !deadCrops.contains(id)) { - return FunctionResult.PASS; - } - ItemStack itemInHand = interactBlockWrapper.getItemInHand(); - Location location = clicked.getLocation(); - Player player = interactBlockWrapper.getPlayer(); - Location potLocation = location.clone().subtract(0,1,0); - // check fertilizer requirements - State state = new State(player, itemInHand, potLocation); - if (!RequirementManager.isRequirementMet(state, fertilizer.getRequirements())) { - return FunctionResult.RETURN; - } - // check pot data - Pot pot = getPotByBlock(potLocation.getBlock()); - if (pot == null) { - LogUtils.warn("Found a crop without pot interacted by player " + player.getName() + " with a fertilizer at " + location); - customProvider.removeAnythingAt(location); - return FunctionResult.RETURN; - } - // check pot use requirements - if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { - return FunctionResult.RETURN; - } - // check whitelist - if (!fertilizer.getPotWhitelist().contains(pot.getKey())) { - fertilizer.trigger(ActionTrigger.WRONG_POT, state); - return FunctionResult.RETURN; - } - // check before plant - if (fertilizer.isBeforePlant()) { - fertilizer.trigger(ActionTrigger.BEFORE_PLANT, state); - return FunctionResult.RETURN; - } - - SimpleLocation simpleLocation = SimpleLocation.of(potLocation); - Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); - boolean hasWater = false; - if (worldPot.isEmpty()) { - plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it."); - MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey()); - plugin.getWorldManager().addPotAt(memoryPot, simpleLocation); - worldPot = Optional.of(memoryPot); - } else { - hasWater = worldPot.get().getWater() > 0; - } - // fire the event - FertilizerUseEvent useEvent = new FertilizerUseEvent(state.getPlayer(), itemInHand, fertilizer, location, worldPot.get()); - if (EventUtils.fireAndCheckCancel(useEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - // add data - plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); - if (interactBlockWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemInHand.getAmount() - 1); - } - fertilizer.trigger(ActionTrigger.USE, state); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL), - - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { - return FunctionResult.PASS; - } - // is a crop - String id = furnitureWrapper.getID(); - Crop crop = getCropByStageID(id); - if (crop == null && !deadCrops.contains(id)) { - return FunctionResult.PASS; - } - ItemStack itemInHand = furnitureWrapper.getItemInHand(); - Location location = furnitureWrapper.getLocation(); - Player player = furnitureWrapper.getPlayer(); - Location potLocation = location.clone().subtract(0,1,0); - // check fertilizer requirements - State state = new State(player, itemInHand, potLocation); - if (!RequirementManager.isRequirementMet(state, fertilizer.getRequirements())) { - return FunctionResult.RETURN; - } - // check pot data - Pot pot = getPotByBlock(potLocation.getBlock()); - if (pot == null) { - LogUtils.warn("Found a crop without pot interacted by player " + player.getName() + " with a fertilizer at " + location); - customProvider.removeAnythingAt(location); - return FunctionResult.RETURN; - } - // check pot use requirements - if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { - return FunctionResult.RETURN; - } - // check whitelist - if (!fertilizer.getPotWhitelist().contains(pot.getKey())) { - fertilizer.trigger(ActionTrigger.WRONG_POT, state); - return FunctionResult.RETURN; - } - // check before plant - if (fertilizer.isBeforePlant()) { - fertilizer.trigger(ActionTrigger.BEFORE_PLANT, state); - return FunctionResult.RETURN; - } - - SimpleLocation simpleLocation = SimpleLocation.of(potLocation); - Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); - boolean hasWater = false; - if (worldPot.isEmpty()) { - plugin.debug("Found pot data not exists at " + potLocation); - } else { - hasWater = worldPot.get().getWater() > 0; - } - - // add data - plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); - if (furnitureWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemInHand.getAmount() - 1); - } - fertilizer.trigger(ActionTrigger.USE, state); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - } - - @SuppressWarnings("DuplicatedCode") - private void loadCrop(String key, ConfigurationSection section) { - ItemCarrier itemCarrier = ItemCarrier.valueOf(section.getString("type").toUpperCase(Locale.ENGLISH)); - if (itemCarrier != ItemCarrier.TRIPWIRE && itemCarrier != ItemCarrier.ITEM_DISPLAY && itemCarrier != ItemCarrier.ITEM_FRAME && itemCarrier != ItemCarrier.NOTE_BLOCK) { - LogUtils.warn("Unsupported crop type: " + itemCarrier.name()); - return; - } - - String seedItemID = section.getString("seed"); - boolean rotation = section.getBoolean("random-rotation", false); - int maxPoints = section.getInt("max-points"); - - ConfigurationSection pointSection = section.getConfigurationSection("points"); - if (pointSection == null) { - LogUtils.warn(key + ".points section can't be null"); - return; - } - - CropConfig crop = new CropConfig( - key, seedItemID, itemCarrier, - new HashSet<>(section.getStringList("pot-whitelist")), rotation, maxPoints, - ConfigUtils.getBoneMeals(section.getConfigurationSection("custom-bone-meal")), - new Conditions(ConfigUtils.getConditions(section.getConfigurationSection("grow-conditions"))), - ConfigUtils.getDeathConditions(section.getConfigurationSection("death-conditions"), itemCarrier), - ConfigUtils.getActionMap(section.getConfigurationSection("events")), - ConfigUtils.getStageConfigs(pointSection), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.plant")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.break")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.interact")) - ); - - if (!this.registerCrop(crop)) { - LogUtils.warn("Failed to register new crop: " + key + " due to duplicated entries."); - return; - } - - for (DeathConditions deathConditions : crop.getDeathConditions()) { - if (deathConditions.getDeathItem() != null) { - deadCrops.add(deathConditions.getDeathItem()); - } - } - - this.registerItemFunction(crop.getSeedItemID(), FunctionTrigger.INTERACT_AT, - /* - * Handle crop planting - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { - return FunctionResult.PASS; - } - // is a pot - Block clicked = blockWrapper.getClickedBlock(); - Pot pot = getPotByBlock(clicked); - if (pot == null) { - return FunctionResult.PASS; - } - // click the upper face - if (blockWrapper.getClickedFace() != BlockFace.UP) { - return FunctionResult.PASS; - } - if (!customProvider.isAir(clicked.getRelative(BlockFace.UP).getLocation())) { - return FunctionResult.RETURN; - } - Player player = blockWrapper.getPlayer(); - ItemStack itemInHand = blockWrapper.getItemInHand(); - Location seedLocation = clicked.getLocation().clone().add(0,1,0); - State state = new State(player, itemInHand, seedLocation); - // check whitelist - if (!crop.getPotWhitelist().contains(pot.getKey())) { - crop.trigger(ActionTrigger.WRONG_POT, state); - return FunctionResult.RETURN; - } - // check plant requirements - if (!RequirementManager.isRequirementMet(state, crop.getPlantRequirements())) { - return FunctionResult.RETURN; - } - // check limitation - SimpleLocation simpleLocation = SimpleLocation.of(seedLocation); - if (plugin.getWorldManager().isReachLimit(simpleLocation, ItemType.CROP)) { - crop.trigger(ActionTrigger.REACH_LIMIT, state); - return FunctionResult.RETURN; - } - // fire event - CropPlantEvent plantEvent = new CropPlantEvent(player, itemInHand, seedLocation, crop, 0); - if (EventUtils.fireAndCheckCancel(plantEvent)) { - return FunctionResult.RETURN; - } - // place the crop - this.placeItem(seedLocation, crop.getItemCarrier(), crop.getStageItemByPoint(plantEvent.getPoint()), crop.hasRotation() ? CRotation.RANDOM : CRotation.NONE); - - // reduce item - if (player.getGameMode() != GameMode.CREATIVE) - itemInHand.setAmount(itemInHand.getAmount() - 1); - crop.trigger(ActionTrigger.PLANT, state); - - plugin.getWorldManager().addCropAt(new MemoryCrop(simpleLocation, crop.getKey(), plantEvent.getPoint()), simpleLocation); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - - for (Crop.Stage stage : crop.getStages()) { - if (stage.getStageID() != null) { - this.registerItemFunction(stage.getStageID(), FunctionTrigger.BE_INTERACTED, - /* - * Add water to pot if player is clicking a crop - * Trigger crop interaction - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { - return FunctionResult.PASS; - } - - Player player = interactWrapper.getPlayer(); - Location cropLocation = LocationUtils.toBlockLocation(interactWrapper.getLocation()); - ItemStack itemInHand = interactWrapper.getItemInHand(); - State cropState = new State(player, itemInHand, cropLocation); - - // check crop interact requirements - if (!RequirementManager.isRequirementMet(cropState, crop.getInteractRequirements())) { - return FunctionResult.RETURN; - } - if (!RequirementManager.isRequirementMet(cropState, stage.getInteractRequirements())) { - return FunctionResult.RETURN; - } - SimpleLocation simpleLocation = SimpleLocation.of(cropLocation); - Optional optionalCrop = plugin.getWorldManager().getCropAt(simpleLocation); - if (optionalCrop.isEmpty()) { - plugin.debug("Found a crop without data interacted by " + player.getName() + " at " + cropLocation); - WorldCrop newCrop = new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint()); - plugin.getWorldManager().addCropAt(newCrop, simpleLocation); - optionalCrop = Optional.of(newCrop); - } else { - if (!optionalCrop.get().getKey().equals(crop.getKey())) { - LogUtils.warn("Found a crop having inconsistent data interacted by " + player.getName() + " at " + cropLocation + "."); - plugin.getWorldManager().addCropAt(new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint()), simpleLocation); - return FunctionResult.RETURN; - } - } - - CropInteractEvent interactEvent = new CropInteractEvent(conditionWrapper.getPlayer(), interactWrapper.getItemInHand(), cropLocation, optionalCrop.orElse(null)); - if (EventUtils.fireAndCheckCancel(interactEvent)) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - - String itemID = getItemID(itemInHand); - int itemAmount = itemInHand.getAmount(); - - Location potLocation = cropLocation.clone().subtract(0,1,0); - Pot pot = getPotByBlock(potLocation.getBlock()); - State potState = new State(player, itemInHand, potLocation); - - // check pot use requirements - if (pot != null && RequirementManager.isRequirementMet(potState, pot.getUseRequirements())) { - // get water in pot - int waterInPot = plugin.getWorldManager().getPotAt(SimpleLocation.of(potLocation)).map(WorldPot::getWater).orElse(0); - // water the pot - for (PassiveFillMethod method : pot.getPassiveFillMethods()) { - if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { - if (method.canFill(potState)) { - if (waterInPot < pot.getStorage()) { - if (player.getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemAmount - method.getUsedAmount()); - if (method.getReturned() != null) { - ItemStack returned = getItemStack(player, method.getReturned()); - ItemUtils.giveItem(player, returned, method.getReturnedAmount()); - } - } - method.trigger(potState); - pot.trigger(ActionTrigger.ADD_WATER, potState); - plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), SimpleLocation.of(potLocation)); - } else { - pot.trigger(ActionTrigger.FULL, potState); - } - } - return FunctionResult.RETURN; - } - } - } - - // if not reached the max point, try detecting bone meals - if (optionalCrop.get().getPoint() < crop.getMaxPoints()) { - for (BoneMeal boneMeal : crop.getBoneMeals()) { - if (boneMeal.getItem().equals(itemID)) { - // fire the event - BoneMealUseEvent useEvent = new BoneMealUseEvent(player, itemInHand, cropLocation, boneMeal, optionalCrop.get()); - if (EventUtils.fireAndCheckCancel(useEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - if (player.getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemAmount - boneMeal.getUsedAmount()); - if (boneMeal.getReturned() != null) { - ItemStack returned = getItemStack(player, boneMeal.getReturned()); - ItemUtils.giveItem(player, returned, boneMeal.getReturnedAmount()); - } - } - boneMeal.trigger(cropState); - plugin.getWorldManager().addPointToCrop(crop, boneMeal.getPoint(), SimpleLocation.of(cropLocation)); - return FunctionResult.RETURN; - } - } - } else { - crop.trigger(ActionTrigger.RIPE, cropState); - } - - // trigger interact actions - crop.trigger(ActionTrigger.INTERACT, cropState); - stage.trigger(ActionTrigger.INTERACT, cropState); - return FunctionResult.PASS; - }, CFunction.FunctionPriority.HIGH) - ); - - this.registerItemFunction(stage.getStageID(), FunctionTrigger.BREAK, - /* - * Break the crop - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof BreakWrapper breakWrapper)) { - return FunctionResult.PASS; - } - Player player = breakWrapper.getPlayer(); - Location cropLocation = LocationUtils.toBlockLocation(breakWrapper.getLocation()); - State state = new State(player, breakWrapper.getItemInHand(), cropLocation); - // check crop break requirements - if (!RequirementManager.isRequirementMet(state, crop.getBreakRequirements())) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - if (!RequirementManager.isRequirementMet(state, stage.getBreakRequirements())) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - SimpleLocation simpleLocation = SimpleLocation.of(cropLocation); - Optional optionalWorldCrop = plugin.getWorldManager().getCropAt(simpleLocation); - if (optionalWorldCrop.isEmpty()) { - WorldCrop worldCrop = new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint()); - plugin.getWorldManager().addCropAt(worldCrop, simpleLocation); - optionalWorldCrop = Optional.of(worldCrop); - plugin.debug("Found a crop without data broken by " + player.getName() + " at " + cropLocation + ". " + - "You can safely ignore this if the crop is spawned in the wild."); - } else { - if (!optionalWorldCrop.get().getKey().equals(crop.getKey())) { - LogUtils.warn("Found a crop having inconsistent data broken by " + player.getName() + " at " + cropLocation + "."); - plugin.getWorldManager().removeCropAt(simpleLocation); - return FunctionResult.RETURN; - } - } - // fire event - CropBreakEvent breakEvent = new CropBreakEvent(player, cropLocation, optionalWorldCrop.get(), Reason.BREAK); - if (EventUtils.fireAndCheckCancel(breakEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - // trigger actions - stage.trigger(ActionTrigger.BREAK, state); - crop.trigger(ActionTrigger.BREAK, state); - plugin.getWorldManager().removeCropAt(simpleLocation); - return FunctionResult.PASS; - }, CFunction.FunctionPriority.NORMAL) - ); - } - } - } - - @SuppressWarnings("DuplicatedCode") - private void loadPot(String key, ConfigurationSection section) { - int storage = section.getInt("max-water-storage", 1); - String dryModel = Preconditions.checkNotNull(section.getString("base.dry"), "base.dry should not be null"); - String wetModel = Preconditions.checkNotNull(section.getString("base.wet"), "base.wet should not be null"); - boolean enableFertilizedAppearance = section.getBoolean("fertilized-pots.enable", false); - - PotConfig pot = new PotConfig( - key, storage, section.getBoolean("absorb-rainwater", false), section.getBoolean("absorb-nearby-water", false), - dryModel, wetModel, - enableFertilizedAppearance, - enableFertilizedAppearance ? ConfigUtils.getFertilizedPotMap(section.getConfigurationSection("fertilized-pots")) : new HashMap<>(), - section.contains("water-bar") ? WaterBar.of( - section.getString("water-bar.left", ""), - section.getString("water-bar.empty", ""), - section.getString("water-bar.full", ""), - section.getString("water-bar.right", "") - ) : null, - ConfigUtils.getPassiveFillMethods(section.getConfigurationSection("fill-method")), - ConfigUtils.getActionMap(section.getConfigurationSection("events")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.place")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.break")), - ConfigUtils.getRequirements(section.getConfigurationSection("requirements.use")) - ); - - if (!this.registerPot(pot)) { - LogUtils.warn("Failed to register new pot: " + key + " due to duplicated entries."); - return; - } - - for (String potItemID : pot.getPotBlocks()) { - this.registerItemFunction(potItemID, FunctionTrigger.BE_INTERACTED, - /* - * Interact the pot - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { - return FunctionResult.PASS; - } - - ItemStack itemInHand = interactBlockWrapper.getItemInHand(); - Player player = interactBlockWrapper.getPlayer(); - Location location = interactBlockWrapper.getClickedBlock().getLocation(); - // check pot use requirement - State state = new State(player, itemInHand, location); - if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { - return FunctionResult.RETURN; - } - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional optionalPot = plugin.getWorldManager().getPotAt(simpleLocation); - if (optionalPot.isEmpty()) { - plugin.debug("Found a pot without data interacted by " + player.getName() + " at " + location); - WorldPot newPot = new MemoryPot(simpleLocation, pot.getKey()); - if (pot.isWetPot(potItemID)) { - newPot.setWater(1); - } - plugin.getWorldManager().addPotAt(newPot, simpleLocation); - optionalPot = Optional.of(newPot); - } else { - if (!optionalPot.get().getKey().equals(pot.getKey())) { - LogUtils.warn("Found a pot having inconsistent data interacted by " + player.getName() + " at " + location + "."); - plugin.getWorldManager().addPotAt(new MemoryPot(simpleLocation, pot.getKey()), simpleLocation); - return FunctionResult.RETURN; - } - } - - // fire the event - PotInteractEvent interactEvent = new PotInteractEvent(player, itemInHand, location, optionalPot.get()); - if (EventUtils.fireAndCheckCancel(interactEvent)) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - String itemID = getItemID(itemInHand); - int itemAmount = itemInHand.getAmount(); - // get water in pot - int waterInPot = plugin.getWorldManager().getPotAt(simpleLocation).map(WorldPot::getWater).orElse(0); - - for (PassiveFillMethod method : pot.getPassiveFillMethods()) { - if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { - if (method.canFill(state)) { - if (waterInPot < pot.getStorage()) { - // fire the event - PotFillEvent waterEvent = new PotFillEvent(player, itemInHand, location, method, optionalPot.get()); - if (EventUtils.fireAndCheckCancel(waterEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - - if (player.getGameMode() != GameMode.CREATIVE) { - itemInHand.setAmount(itemAmount - method.getUsedAmount()); - if (method.getReturned() != null) { - ItemStack returned = getItemStack(player, method.getReturned()); - ItemUtils.giveItem(player, returned, method.getReturnedAmount()); - } - } - method.trigger(state); - pot.trigger(ActionTrigger.ADD_WATER, state); - plugin.getWorldManager().addWaterToPot(pot, method.getAmount(), simpleLocation); - } else { - pot.trigger(ActionTrigger.FULL, state); - return FunctionResult.RETURN; - } - } - return FunctionResult.RETURN; - } - } - - return FunctionResult.PASS; - }, CFunction.FunctionPriority.NORMAL) - ); - - this.registerItemFunction(potItemID, FunctionTrigger.BE_INTERACTED, - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { - return FunctionResult.PASS; - } - - Location location = interactBlockWrapper.getClickedBlock().getLocation(); - plugin.getScheduler().runTaskSyncLater(() -> { - Optional worldPot = plugin.getWorldManager().getPotAt(SimpleLocation.of(location)); - if (worldPot.isEmpty()) { - return; - } - State state = new State(interactBlockWrapper.getPlayer(), interactBlockWrapper.getItemInHand(), location); - state.setArg("{current}", String.valueOf(worldPot.get().getWater())); - state.setArg("{storage}", String.valueOf(pot.getStorage())); - state.setArg("{water_bar}", pot.getWaterBar() == null ? "" : pot.getWaterBar().getWaterBar(worldPot.get().getWater(), pot.getStorage())); - state.setArg("{left_times}", String.valueOf(worldPot.get().getFertilizerTimes())); - state.setArg("{max_times}", String.valueOf(Optional.ofNullable(worldPot.get().getFertilizer()).map(fertilizer -> fertilizer.getTimes()).orElse(0))); - state.setArg("{icon}", Optional.ofNullable(worldPot.get().getFertilizer()).map(fertilizer -> fertilizer.getIcon()).orElse("")); - - // trigger actions - pot.trigger(ActionTrigger.INTERACT, state); - }, location, 1); - return FunctionResult.PASS; - }, CFunction.FunctionPriority.LOWEST) - ); - - this.registerItemFunction(potItemID, FunctionTrigger.BREAK, - /* - * Break the pot - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof BreakBlockWrapper blockWrapper)) { - return FunctionResult.PASS; - } - // check break requirements - Location location = blockWrapper.getBrokenBlock().getLocation(); - Player player = blockWrapper.getPlayer(); - State state = new State(player, blockWrapper.getItemInHand(), location); - if (!RequirementManager.isRequirementMet(state, pot.getBreakRequirements())) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - Location cropLocation = location.clone().add(0,1,0); - String cropStageID = customProvider.getSomethingAt(cropLocation); - // remove crops - Crop.Stage stage = stage2CropStageMap.get(cropStageID); - if (stage != null) { - // if crops are above, check the break requirements for crops - Crop crop = getCropByStageID(cropStageID); - if (crop != null) { - State cropState = new State(player, blockWrapper.getItemInHand(), cropLocation); - if (!RequirementManager.isRequirementMet(cropState, crop.getBreakRequirements())) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - if (!RequirementManager.isRequirementMet(cropState, stage.getBreakRequirements())) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - SimpleLocation simpleLocation = SimpleLocation.of(cropLocation); - Optional optionalWorldCrop = plugin.getWorldManager().getCropAt(simpleLocation); - if (optionalWorldCrop.isPresent()) { - if (!optionalWorldCrop.get().getKey().equals(crop.getKey())) { - LogUtils.warn("Found a crop having inconsistent data broken by " + player.getName() + " at " + cropLocation + "."); - } - } else { - WorldCrop worldCrop = new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint()); - optionalWorldCrop = Optional.of(worldCrop); - plugin.getWorldManager().addCropAt(worldCrop, simpleLocation); - plugin.debug("Found a crop without data broken by " + player.getName() + " at " + cropLocation + ". " + - "You can safely ignore this if the crop is spawned in the wild."); - } - // fire event - CropBreakEvent breakEvent = new CropBreakEvent(player, cropLocation, optionalWorldCrop.get(), Reason.BREAK); - if (EventUtils.fireAndCheckCancel(breakEvent)) - return FunctionResult.CANCEL_EVENT_AND_RETURN; - // trigger actions - stage.trigger(ActionTrigger.BREAK, cropState); - crop.trigger(ActionTrigger.BREAK, cropState); - plugin.getWorldManager().removeCropAt(simpleLocation); - customProvider.removeAnythingAt(cropLocation); - } else { - LogUtils.warn("Invalid crop stage: " + cropStageID); - customProvider.removeAnythingAt(cropLocation); - } - } - // remove dead crops - if (deadCrops.contains(cropStageID)) { - customProvider.removeAnythingAt(cropLocation); - } - - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional optionalPot = plugin.getWorldManager().getPotAt(simpleLocation); - if (optionalPot.isEmpty()) { - plugin.debug("Found a pot without data broken by " + state.getPlayer().getName() + " at " + simpleLocation); - return FunctionResult.RETURN; - } - if (!optionalPot.get().getKey().equals(pot.getKey())) { - plugin.debug("Found a pot having inconsistent data broken by " + state.getPlayer().getName() + " at " + simpleLocation + "."); - plugin.getWorldManager().removePotAt(simpleLocation); - return FunctionResult.RETURN; - } - // fire event - PotBreakEvent breakEvent = new PotBreakEvent(blockWrapper.getPlayer(), location, optionalPot.get(), Reason.BREAK); - if (EventUtils.fireAndCheckCancel(breakEvent)) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // remove data - plugin.getWorldManager().removePotAt(simpleLocation); - pot.trigger(ActionTrigger.BREAK, state); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL) - ); - - this.registerItemFunction(potItemID, FunctionTrigger.PLACE, - /* - * Place the pot - */ - new CFunction(conditionWrapper -> { - if (!(conditionWrapper instanceof PlaceBlockWrapper blockWrapper)) { - return FunctionResult.PASS; - } - Location location = blockWrapper.getPlacedBlock().getLocation(); - Player player = blockWrapper.getPlayer(); - // check place requirements - State state = new State(player, blockWrapper.getItemInHand(), location); - if (!RequirementManager.isRequirementMet(state, pot.getPlaceRequirements())) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // check limitation - SimpleLocation simpleLocation = SimpleLocation.of(location); - if (plugin.getWorldManager().isReachLimit(simpleLocation, ItemType.POT)) { - pot.trigger(ActionTrigger.REACH_LIMIT, new State(player, blockWrapper.getItemInHand(), location)); - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // fire event - PotPlaceEvent potPlaceEvent = new PotPlaceEvent(player, location, pot); - if (EventUtils.fireAndCheckCancel(potPlaceEvent)) { - return FunctionResult.CANCEL_EVENT_AND_RETURN; - } - // add data - plugin.getWorldManager().addPotAt(new MemoryPot(simpleLocation, pot.getKey()), simpleLocation); - pot.trigger(ActionTrigger.PLACE, state); - return FunctionResult.RETURN; - }, CFunction.FunctionPriority.NORMAL)); - } - } - - private void registerItemFunction(String[] items, FunctionTrigger trigger, CFunction... function) { - for (String item : items) { - if (item != null) { - registerItemFunction(item, trigger, function); - } - } - } - - private void registerItemFunction(String item, FunctionTrigger trigger, CFunction... function) { - if (item == null) return; - if (itemID2FunctionMap.containsKey(item)) { - var previous = itemID2FunctionMap.get(item); - TreeSet previousFunctions = previous.get(trigger); - if (previousFunctions == null) { - previous.put(trigger, new TreeSet<>(List.of(function))); - } else { - previousFunctions.addAll(List.of(function)); - } - } else { - TreeSet list = new TreeSet<>(List.of(function)); - itemID2FunctionMap.put(item, new HashMap<>(Map.of(trigger, list))); - } - } - - @Override - @SuppressWarnings("DuplicatedCode") - public void handlePlayerInteractBlock( - Player player, - Block clickedBlock, - BlockFace clickedFace, - Cancellable event - ) { - if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) - return; - - // check anti-grief - if (!antiGrief.canInteract(player, clickedBlock.getLocation())) - return; - - // check pot firstly because events might be cancelled - var condition = new InteractBlockWrapper(player, clickedBlock, clickedFace); - String blockID = customProvider.getBlockID(clickedBlock); - TreeSet blockFunctions = Optional.ofNullable(itemID2FunctionMap.get(blockID)) - .map(map -> map.get(FunctionTrigger.BE_INTERACTED)) - .orElse(null); - if (handleFunctions(blockFunctions, condition, event)) { - return; - } - // Then check item in hand - String itemID = getItemID(condition.getItemInHand()); - Optional.ofNullable(itemID2FunctionMap.get(itemID)) - .map(map -> map.get(FunctionTrigger.INTERACT_AT)) - .ifPresent(itemFunctions -> handleFunctions(itemFunctions, condition, event)); - } - - @Override - public void handlePlayerInteractAir( - Player player, - Cancellable event - ) { - if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) - return; - - // check anti-grief - if (!antiGrief.canInteract(player, player.getLocation())) - return; - - var condition = new InteractWrapper(player, null); - // check item in hand - String itemID = getItemID(condition.getItemInHand()); - Optional.ofNullable(itemID2FunctionMap.get(itemID)) - .map(map -> map.get(FunctionTrigger.INTERACT_AIR)) - .ifPresent(cFunctions -> handleFunctions(cFunctions, condition, event)); - } - - @Override - public void handlePlayerBreakBlock( - Player player, - Block brokenBlock, - String blockID, - Cancellable event - ) { - if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) - return; - - // check anti-grief - if (!antiGrief.canBreak(player, brokenBlock.getLocation())) - return; - - Optional.ofNullable(itemID2FunctionMap.get(blockID)) - .map(map -> map.get(FunctionTrigger.BREAK)) - .ifPresent(cFunctions -> handleFunctions(cFunctions, new BreakBlockWrapper(player, brokenBlock), event)); - } - - @Override - @SuppressWarnings("DuplicatedCode") - public void handlePlayerInteractFurniture( - Player player, - Location location, - String id, - Entity baseEntity, - Cancellable event - ) { - if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) - return; - - // check anti-grief - if (!antiGrief.canInteract(player, location)) - return; - - var condition = new InteractFurnitureWrapper(player, location, id, baseEntity); - // check furniture firstly - TreeSet functions = Optional.ofNullable(itemID2FunctionMap.get(id)).map(map -> map.get(FunctionTrigger.BE_INTERACTED)).orElse(null); - if (handleFunctions(functions, condition, event)) { - return; - } - // Then check item in hand - String itemID = getItemID(condition.getItemInHand()); - Optional.ofNullable(itemID2FunctionMap.get(itemID)) - .map(map -> map.get(FunctionTrigger.INTERACT_AT)) - .ifPresent(cFunctions -> handleFunctions(cFunctions, condition, event)); - } - - @Override - public void handlePlayerPlaceFurniture( - Player player, - Location location, - String id, - Cancellable event - ) { - if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) - return; - - // check anti-grief - if (!antiGrief.canPlace(player, location)) - return; - - // check furniture, no need to check item in hand - Optional.ofNullable(itemID2FunctionMap.get(id)) - .map(map -> map.get(FunctionTrigger.PLACE)) - .ifPresent(cFunctions -> handleFunctions(cFunctions, new PlaceFurnitureWrapper(player, location, id), event)); - } - - @Override - public void handlePlayerBreakFurniture( - Player player, - Location location, - String id, - Cancellable event - ) { - if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) - return; - - // check anti-grief - if (!antiGrief.canBreak(player, location)) - return; - - // check furniture, no need to check item in hand - Optional.ofNullable(itemID2FunctionMap.get(id)) - .map(map -> map.get(FunctionTrigger.BREAK)) - .ifPresent(cFunctions -> handleFunctions(cFunctions, new BreakFurnitureWrapper(player, location, id), event)); - } - - @Override - public void handlePlayerPlaceBlock(Player player, Block block, String blockID, Cancellable event) { - if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) - return; - - // check anti-grief - if (!antiGrief.canPlace(player, block.getLocation())) - return; - - // check furniture, no need to check item in hand - Optional.ofNullable(itemID2FunctionMap.get(blockID)) - .map(map -> map.get(FunctionTrigger.PLACE)) - .ifPresent(cFunctions -> handleFunctions(cFunctions, new PlaceBlockWrapper(player, block, blockID), event)); - } - - @Override - public void handleEntityTramplingBlock(Entity entity, Block block, Cancellable event) { - if (entity instanceof Player player) { - handlePlayerBreakBlock(player, block, "FARMLAND", event); - } else { - // if the block is a pot - Pot pot = getPotByBlock(block); - if (pot != null) { - Location potLocation = block.getLocation(); - // get or fix pot - SimpleLocation potSimpleLocation = SimpleLocation.of(potLocation); - Optional worldPot = plugin.getWorldManager().getPotAt(potSimpleLocation); - if (worldPot.isEmpty()) { - plugin.debug("Found pot data not exists at " + potSimpleLocation + ". Fixing it."); - MemoryPot memoryPot = new MemoryPot(potSimpleLocation, pot.getKey()); - plugin.getWorldManager().addPotAt(memoryPot, potSimpleLocation); - worldPot = Optional.of(memoryPot); - } - // fire the event - PotBreakEvent potBreakEvent = new PotBreakEvent(entity, potLocation, worldPot.get(), Reason.TRAMPLE); - if (EventUtils.fireAndCheckCancel(potBreakEvent)) { - event.setCancelled(true); - return; - } - - plugin.getWorldManager().removePotAt(SimpleLocation.of(block.getLocation())); - pot.trigger(ActionTrigger.BREAK, new State(null, new ItemStack(Material.AIR), block.getLocation())); - - Location cropLocation = block.getLocation().clone().add(0,1,0); - String cropStageID = customProvider.getSomethingAt(cropLocation); - Crop.Stage stage = stage2CropStageMap.get(cropStageID); - if (stage != null) { - Crop crop = getCropByStageID(cropStageID); - SimpleLocation simpleLocation = SimpleLocation.of(cropLocation); - Optional optionalWorldCrop = plugin.getWorldManager().getCropAt(simpleLocation); - if (optionalWorldCrop.isPresent()) { - if (!optionalWorldCrop.get().getKey().equals(crop.getKey())) { - LogUtils.warn("Found a crop having inconsistent data broken by " + entity.getType() + " at " + cropLocation + "."); - } - } else { - WorldCrop worldCrop = new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint()); - plugin.getWorldManager().addCropAt(worldCrop, simpleLocation); - optionalWorldCrop = Optional.of(worldCrop); - plugin.debug("Found a crop without data broken by " + entity.getType() + " at " + cropLocation + ". " + - "You can safely ignore this if the crop is spawned in the wild."); - } - // fire the event - CropBreakEvent breakEvent = new CropBreakEvent(entity, cropLocation, optionalWorldCrop.get(), Reason.TRAMPLE); - if (EventUtils.fireAndCheckCancel(breakEvent)) { - event.setCancelled(true); - return; - } - - State state = new State(null, new ItemStack(Material.AIR), cropLocation); - // trigger actions - stage.trigger(ActionTrigger.BREAK, state); - crop.trigger(ActionTrigger.BREAK, state); - plugin.getWorldManager().removeCropAt(simpleLocation); - customProvider.removeAnythingAt(cropLocation); - } - - if (deadCrops.contains(cropStageID)) { - customProvider.removeAnythingAt(cropLocation); - } - } - } - } - - @Override - public void handleExplosion(Entity entity, List blocks, Cancellable event) { - List locationsToRemove = new ArrayList<>(); - List locationsToRemoveBlock = new ArrayList<>(); - HashSet blockLocations = new HashSet<>(blocks.stream().map(Block::getLocation).toList()); - List aboveLocations = new ArrayList<>(); - for (Location location : blockLocations) { - Optional optionalCustomCropsBlock = plugin.getWorldManager().getBlockAt(SimpleLocation.of(location)); - if (optionalCustomCropsBlock.isPresent()) { - Event customEvent = null; - CustomCropsBlock customCropsBlock = optionalCustomCropsBlock.get(); - switch (customCropsBlock.getType()) { - case POT -> { - customEvent = new PotBreakEvent(entity, location, (WorldPot) customCropsBlock, Reason.EXPLODE); - Location above = location.clone().add(0,1,0); - if (!blockLocations.contains(above)) { - aboveLocations.add(above); - } - } - case SPRINKLER -> customEvent = new SprinklerBreakEvent(entity, location, (WorldSprinkler) customCropsBlock, Reason.EXPLODE); - case CROP -> customEvent = new CropBreakEvent(entity, location, (WorldCrop) customCropsBlock, Reason.EXPLODE); - case GREENHOUSE -> customEvent = new GreenhouseGlassBreakEvent(entity, location, (WorldGlass) customCropsBlock, Reason.EXPLODE); - case SCARECROW -> customEvent = new ScarecrowBreakEvent(entity, location, (WorldScarecrow) customCropsBlock, Reason.EXPLODE); - } - if (customEvent != null && EventUtils.fireAndCheckCancel(customEvent)) { - event.setCancelled(true); - return; - } - locationsToRemove.add(location); - } - } - - for (Location location : aboveLocations) { - Optional optionalCustomCropsBlock = plugin.getWorldManager().getBlockAt(SimpleLocation.of(location)); - if (optionalCustomCropsBlock.isPresent()) { - CustomCropsBlock customCropsBlock = optionalCustomCropsBlock.get(); - if (customCropsBlock.getType() == ItemType.CROP) { - if (EventUtils.fireAndCheckCancel(new CropBreakEvent(entity, location, (WorldCrop) customCropsBlock, Reason.EXPLODE))) { - event.setCancelled(true); - return; - } - locationsToRemove.add(location); - locationsToRemoveBlock.add(location); - } - } - } - - for (Location location : locationsToRemoveBlock) { - removeAnythingAt(location); - } - - for (Location location : locationsToRemove) { - CustomCropsBlock customCropsBlock = plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(location)); - if (customCropsBlock != null) { - State state = new State(null, new ItemStack(Material.AIR), location); - switch (customCropsBlock.getType()) { - case POT -> { - Pot pot = ((WorldPot) customCropsBlock).getConfig(); - pot.trigger(ActionTrigger.BREAK, state); - } - case CROP -> { - Crop crop = ((WorldCrop) customCropsBlock).getConfig(); - Crop.Stage stage = crop.getStageByItemID(crop.getStageItemByPoint(((WorldCrop) customCropsBlock).getPoint())); - crop.trigger(ActionTrigger.BREAK, state); - stage.trigger(ActionTrigger.BREAK, state); - } - case SPRINKLER -> { - Sprinkler sprinkler = ((WorldSprinkler) customCropsBlock).getConfig(); - sprinkler.trigger(ActionTrigger.BREAK, state); - } - } - } - } - } - - private boolean handleFunctions(Collection functions, ConditionWrapper wrapper, @Nullable Cancellable event) { - if (functions == null) return false; - for (CFunction function : functions) { - FunctionResult result = function.apply(wrapper); - if (result == FunctionResult.CANCEL_EVENT_AND_RETURN) { - if (event != null) event.setCancelled(true); - return true; - } - if (result == FunctionResult.RETURN) - return true; - } - return false; - } - - @Override - public void updatePotState(Location location, Pot pot, boolean hasWater, Fertilizer fertilizer) { - Block block = location.getBlock(); - Pot currentPot = getPotByBlock(block); - if (currentPot != pot) { - plugin.getWorldManager().removePotAt(SimpleLocation.of(location)); - if (currentPot != null) { - plugin.getWorldManager().addPotAt(new MemoryPot(SimpleLocation.of(location), currentPot.getKey()), SimpleLocation.of(location)); - } - return; - } - if (pot.isVanillaBlock()) { - if (block.getBlockData() instanceof Farmland farmland) { - farmland.setMoisture(hasWater ? 7 : 0); - block.setBlockData(farmland); - return; - } - } - this.customProvider.placeBlock( - location, - pot.getBlockState( - hasWater, - Optional.ofNullable(fertilizer) - .map(Fertilizer::getFertilizerType) - .orElse(null) - ) - ); - } - - @NotNull - @Override - public Collection getPotInRange(Location baseLocation, int width, int length, float yaw, String potID) { - ArrayList potLocations = new ArrayList<>(); - int extend = (width-1) / 2; - int extra = (width-1) % 2; - switch ((int) ((yaw + 180) / 45)) { - case 0 -> { - // -180 ~ -135 - for (int i = -extend; i <= extend + extra; i++) { - for (int j = 0; j < length; j++) { - potLocations.add(baseLocation.clone().add(i, 0, -j)); - } - } - } - case 1 -> { - // -135 ~ -90 - for (int i = -extend - extra; i <= extend; i++) { - for (int j = 0; j < length; j++) { - potLocations.add(baseLocation.clone().add(j, 0, i)); - } - } - } - case 2 -> { - // -90 ~ -45 - for (int i = -extend; i <= extend + extra; i++) { - for (int j = 0; j < length; j++) { - potLocations.add(baseLocation.clone().add(j, 0, i)); - } - } - } - case 3 -> { - // -45 ~ 0 - for (int i = -extend; i <= extend + extra; i++) { - for (int j = 0; j < length; j++) { - potLocations.add(baseLocation.clone().add(i, 0, j)); - } - } - } - case 4 -> { - // 0 ~ 45 - for (int i = -extend - extra; i <= extend; i++) { - for (int j = 0; j < length; j++) { - potLocations.add(baseLocation.clone().add(i, 0, j)); - } - } - } - case 5 -> { - // 45 ~ 90 - for (int i = -extend; i <= extend + extra; i++) { - for (int j = 0; j < length; j++) { - potLocations.add(baseLocation.clone().add(-j, 0, i)); - } - } - } - case 6 -> { - // 90 ~ 135 - for (int i = -extend - extra; i <= extend; i++) { - for (int j = 0; j < length; j++) { - potLocations.add(baseLocation.clone().add(-j, 0, i)); - } - } - } - case 7 -> { - // 135 ~ 180 - for (int i = -extend - extra; i <= extend; i++) { - for (int j = 0; j < length; j++) { - potLocations.add(baseLocation.clone().add(i, 0, -j)); - } - } - } - default -> potLocations.add(baseLocation); - } - return potLocations.stream().filter(it -> { - Pot pot = getPotByBlock(it.getBlock()); - if (pot == null) return false; - return pot.getKey().equals(potID); - }).toList(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java deleted file mode 100644 index 5f6020de9..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleListener.java +++ /dev/null @@ -1,49 +0,0 @@ -///* -// * Copyright (C) <2022> -// * -// * 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.mechanic.item.custom.crucible; -// -//import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -//import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; -//import org.bukkit.event.EventHandler; -// -//public class CrucibleListener extends AbstractCustomListener { -// -// public CrucibleListener(ItemManagerImpl itemManager) { -// super(itemManager); -// } -// -// @EventHandler (ignoreCancelled = true) -// public void onBreakCustomBlock() { -// } -// -// @EventHandler (ignoreCancelled = true) -// public void onPlaceCustomBlock() { -// } -// -// @EventHandler (ignoreCancelled = true) -// public void onPlaceFurniture() { -// } -// -// @EventHandler (ignoreCancelled = true) -// public void onBreakFurniture() { -// } -// -// @EventHandler (ignoreCancelled = true) -// public void onInteractFurniture() { -// } -//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java deleted file mode 100644 index af4d7db17..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/crucible/CrucibleProvider.java +++ /dev/null @@ -1,124 +0,0 @@ -///* -// * Copyright (C) <2022> -// * -// * 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.mechanic.item.custom.crucible; -// -//import io.lumine.mythic.bukkit.BukkitAdapter; -//import io.lumine.mythic.bukkit.adapters.BukkitEntity; -//import io.lumine.mythiccrucible.MythicCrucible; -//import io.lumine.mythiccrucible.items.CrucibleItem; -//import io.lumine.mythiccrucible.items.ItemManager; -//import io.lumine.mythiccrucible.items.blocks.CustomBlockItemContext; -//import io.lumine.mythiccrucible.items.blocks.CustomBlockManager; -//import io.lumine.mythiccrucible.items.furniture.Furniture; -//import io.lumine.mythiccrucible.items.furniture.FurnitureManager; -//import net.momirealms.customcrops.api.util.LogUtils; -//import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; -//import org.bukkit.Location; -//import org.bukkit.Material; -//import org.bukkit.block.Block; -//import org.bukkit.block.BlockFace; -//import org.bukkit.entity.Entity; -//import org.bukkit.inventory.ItemStack; -// -//import java.util.Optional; -// -//public class CrucibleProvider implements CustomProvider { -// -// private final ItemManager itemManager; -// private final CustomBlockManager blockManager; -// private final FurnitureManager furnitureManager; -// -// public CrucibleProvider() { -// this.itemManager = MythicCrucible.inst().getItemManager(); -// this.blockManager = itemManager.getCustomBlockManager(); -// this.furnitureManager = itemManager.getFurnitureManager(); -// } -// -// @Override -// public boolean removeBlock(Location location) { -// Block block = location.getBlock(); -// if (block.getType() == Material.AIR) { -// return false; -// } -// Optional optional = blockManager.getBlockFromBlock(block); -// if (optional.isPresent()) { -// optional.get().remove(block, null, false); -// } else { -// block.setType(Material.AIR); -// } -// return true; -// } -// -// @Override -// public void placeCustomBlock(Location location, String id) { -// Optional optionalCI = itemManager.getItem(id); -// if (optionalCI.isPresent()) { -// location.getBlock().setBlockData(optionalCI.get().getBlockData().getBlockData()); -// } else { -// LogUtils.warn("Custom block(" + id +") doesn't exist in Crucible configs. Please double check if that block exists."); -// } -// } -// -// @Override -// public Entity placeFurniture(Location location, String id) { -// Optional optionalCI = itemManager.getItem(id); -// if (optionalCI.isPresent()) { -// return optionalCI.get().getFurnitureData().placeFrame(location.getBlock(), BlockFace.UP, 0f, null); -// } else { -// LogUtils.warn("Furniture(" + id +") doesn't exist in Crucible configs. Please double check if that furniture exists."); -// return null; -// } -// } -// -// @Override -// public void removeFurniture(Entity entity) { -// Optional optional = furnitureManager.getFurniture(entity.getUniqueId()); -// optional.ifPresent(furniture -> furniture.getFurnitureData().remove(furniture, null, false, false)); -// } -// -// @Override -// public String getBlockID(Block block) { -// Optional optionalCB = blockManager.getBlockFromBlock(block); -// return optionalCB.map(customBlockItemContext -> customBlockItemContext.getCrucibleItem().getInternalName()).orElse(block.getType().name()); -// } -// -// @Override -// public String getItemID(ItemStack itemStack) { -// return itemManager.getItem(itemStack).map(CrucibleItem::getInternalName).orElse(null); -// } -// -// @Override -// public ItemStack getItemStack(String id) { -// Optional optionalCI = itemManager.getItem(id); -// return optionalCI.map(crucibleItem -> BukkitAdapter.adapt(crucibleItem.getMythicItem().generateItemStack(1))).orElse(null); -// } -// -// @Override -// public String getEntityID(Entity entity) { -// Optional optionalCI = furnitureManager.getItemFromEntity(entity); -// if (optionalCI.isPresent()) { -// return optionalCI.get().getInternalName(); -// } -// return entity.getType().name(); -// } -// -// @Override -// public boolean isFurniture(Entity entity) { -// return furnitureManager.isFurniture(new BukkitEntity(entity)); -// } -//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java deleted file mode 100644 index 86040ff28..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom.itemsadder; - -import dev.lone.itemsadder.api.CustomFurniture; -import dev.lone.itemsadder.api.Events.*; -import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener; -import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; -import org.bukkit.entity.Entity; -import org.bukkit.event.EventHandler; - -public class ItemsAdderListener extends AbstractCustomListener { - - public ItemsAdderListener(ItemManagerImpl itemManager) { - super(itemManager); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakCustomBlock(CustomBlockBreakEvent event) { - this.itemManager.handlePlayerBreakBlock( - event.getPlayer(), - event.getBlock(), - event.getNamespacedID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceCustomBlock(CustomBlockPlaceEvent event) { - super.onPlaceBlock( - event.getPlayer(), - event.getBlock(), - event.getNamespacedID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceFurniture(FurniturePlaceSuccessEvent event) { - Entity entity = event.getBukkitEntity(); - if (entity == null) return; - // player would be null if furniture is placed with API - if (event.getPlayer() == null) return; - super.onPlaceFurniture( - event.getPlayer(), - entity.getLocation(), - event.getNamespacedID(), - null - ); - } - - @EventHandler (ignoreCancelled = true) - public void onBreakFurniture(FurnitureBreakEvent event) { - CustomFurniture customFurniture = event.getFurniture(); - if (customFurniture == null) return; - Entity entity = customFurniture.getEntity(); - if (entity == null) return; - super.onBreakFurniture( - event.getPlayer(), - entity.getLocation(), - event.getNamespacedID(), - event - ); - } - - @EventHandler (ignoreCancelled = true) - public void onInteractFurniture(FurnitureInteractEvent event) { - CustomFurniture customFurniture = event.getFurniture(); - if (customFurniture == null) return; - Entity entity = customFurniture.getEntity(); - if (entity == null) return; - super.onInteractFurniture(event.getPlayer(), - entity.getLocation(), - event.getNamespacedID(), - entity, - event - ); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java deleted file mode 100644 index bc184d342..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.custom.itemsadder; - -import dev.lone.itemsadder.api.CustomBlock; -import dev.lone.itemsadder.api.CustomFurniture; -import dev.lone.itemsadder.api.CustomStack; -import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.inventory.ItemStack; - -public class ItemsAdderProvider implements CustomProvider { - - @Override - public boolean removeBlock(Location location) { - Block block = location.getBlock(); - if (block.getType() == Material.AIR) - return false; - if (!CustomBlock.remove(location)) { - block.setType(Material.AIR); - } - return true; - } - - @Override - public void placeCustomBlock(Location location, String id) { - CustomBlock block = CustomBlock.place(id, location); - if (block == null) { - LogUtils.warn("Detected that custom block(" + id + ") doesn't exist in ItemsAdder configs. Please double check if that block exists."); - } - } - - @Override - public Entity placeFurniture(Location location, String id) { - try { - CustomFurniture furniture = CustomFurniture.spawnPreciseNonSolid(id, location); - if (furniture == null) return null; - return furniture.getEntity(); - } catch (RuntimeException e) { - LogUtils.warn("Failed to place ItemsAdder furniture. If this is not a problem caused by furniture not existing, consider increasing max-furniture-vehicles-per-chunk in ItemsAdder config.yml."); - e.printStackTrace(); - return null; - } - } - - @Override - public void removeFurniture(Entity entity) { - CustomFurniture.remove(entity, false); - } - - @Override - public String getBlockID(Block block) { - CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); - if (customBlock == null) { - return block.getType().name(); - } - return customBlock.getNamespacedID(); - } - - @Override - public String getItemID(ItemStack itemInHand) { - CustomStack customStack = CustomStack.byItemStack(itemInHand); - if (customStack == null) { - return null; - } - return customStack.getNamespacedID(); - } - - @Override - public ItemStack getItemStack(String id) { - if (id == null) return new ItemStack(Material.AIR); - CustomStack customStack = CustomStack.getInstance(id); - if (customStack == null) { - return null; - } - return customStack.getItemStack().clone(); - } - - @Override - public String getEntityID(Entity entity) { - CustomFurniture customFurniture = CustomFurniture.byAlreadySpawned(entity); - if (customFurniture == null) { - return entity.getType().name(); - } - return customFurniture.getNamespacedID(); - } - - @Override - public boolean isFurniture(Entity entity) { - try { - return CustomFurniture.byAlreadySpawned(entity) != null; - } catch (Exception e) { - return false; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ComponentKeys.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ComponentKeys.java deleted file mode 100644 index 32eaad264..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/ComponentKeys.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.momirealms.customcrops.mechanic.item.factory; - -import net.kyori.adventure.key.Key; - -public class ComponentKeys { - - public static final String CUSTOM_MODEL_DATA = Key.key("minecraft", "custom_model_data").asString(); - public static final String MAX_DAMAGE = Key.key("minecraft", "max_damage").asString(); - public static final String CUSTOM_NAME = Key.key("minecraft", "custom_name").asString(); - public static final String LORE = Key.key("minecraft", "lore").asString(); - public static final String DAMAGE = Key.key("minecraft", "damage").asString(); - public static final String ENCHANTMENT_GLINT_OVERRIDE = Key.key("minecraft", "enchantment_glint_override").asString(); - public static final String HIDE_TOOLTIP = Key.key("minecraft", "hide_tooltip").asString(); - public static final String MAX_STACK_SIZE = Key.key("minecraft", "max_stack_size").asString(); - public static final String PROFILE = Key.key("minecraft", "profile").asString(); - public static final String UNBREAKABLE = Key.key("minecraft", "unbreakable").asString(); -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/Item.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/Item.java deleted file mode 100644 index 35e2233bf..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/factory/Item.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.momirealms.customcrops.mechanic.item.factory; - -import java.util.List; -import java.util.Optional; - -public interface Item { - - Item customModelData(Integer data); - - Optional customModelData(); - - Item damage(Integer data); - - Optional damage(); - - Item maxDamage(Integer data); - - Optional maxDamage(); - - Item lore(List lore); - - Optional> lore(); - - Optional getTag(Object... path); - - Item setTag(Object value, Object... path); - - boolean hasTag(Object... path); - - boolean removeTag(Object... path); - - I getItem(); - - I load(); - - I loadCopy(); - - void update(); -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/CFunction.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/CFunction.java deleted file mode 100644 index 23a83db68..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/CFunction.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.function; - -import net.momirealms.customcrops.mechanic.item.function.wrapper.ConditionWrapper; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Function; - -public class CFunction implements Comparable { - - private static int functionID = 0; - private final Function function; - private final FunctionPriority priority; - private final int id; - - public CFunction(Function function, FunctionPriority priority) { - this.function = function; - this.priority = priority; - this.id = functionID++; - } - - public FunctionResult apply(ConditionWrapper wrapper) { - return function.apply(wrapper); - } - - public Function getFunction() { - return function; - } - - public FunctionPriority getPriority() { - return priority; - } - - @Override - public int compareTo(@NotNull CFunction o) { - if (this.priority.ordinal() > o.priority.ordinal()) { - return 1; - } else if (this.priority.ordinal() < o.priority.ordinal()) { - return -1; - } - return Integer.compare(this.id, o.id); - } - - public static void resetID() { - functionID = 0; - } - - public enum FunctionPriority { - - HIGHEST, - HIGH, - NORMAL, - LOW, - LOWEST; - - public boolean isHigherThan(FunctionPriority priority) { - return this.ordinal() < priority.ordinal(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakWrapper.java deleted file mode 100644 index 90c0d75cf..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakWrapper.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.function.wrapper; - -import org.bukkit.Location; -import org.bukkit.entity.Player; - -public class BreakWrapper extends ConditionWrapper { - - private final Location location; - - public BreakWrapper(Player player, Location location) { - super(player); - this.location = location; - } - - public Location getLocation() { - return location; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/ConditionWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/ConditionWrapper.java deleted file mode 100644 index 2cada638d..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/ConditionWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.function.wrapper; - -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -public abstract class ConditionWrapper { - - private final Player player; - private final ItemStack itemInHand; - - public ConditionWrapper(Player player) { - this.player = player; - this.itemInHand = player.getInventory().getItemInMainHand(); - } - - public Player getPlayer() { - return player; - } - - public ItemStack getItemInHand() { - return itemInHand; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractBlockWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractBlockWrapper.java deleted file mode 100644 index 77f9a6479..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractBlockWrapper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.function.wrapper; - -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Player; - -public class InteractBlockWrapper extends InteractWrapper { - - private final Block clickedBlock; - private final BlockFace clickedFace; - - public InteractBlockWrapper(Player player, Block clickedBlock, BlockFace clickedFace) { - super(player, clickedBlock.getLocation()); - this.clickedBlock = clickedBlock; - this.clickedFace = clickedFace; - } - - public Block getClickedBlock() { - return clickedBlock; - } - - public BlockFace getClickedFace() { - return clickedFace; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractFurnitureWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractFurnitureWrapper.java deleted file mode 100644 index f01e48714..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractFurnitureWrapper.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.function.wrapper; - -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class InteractFurnitureWrapper extends InteractWrapper { - - private final String id; - private final Entity entity; - - public InteractFurnitureWrapper(Player player, Location location, String id, @Nullable Entity baseEntity) { - super(player, location); - this.entity = baseEntity; - this.id = id; - } - - @Nullable - public Entity getEntity() { - return entity; - } - - @NotNull - public String getID() { - return id; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractWrapper.java deleted file mode 100644 index f14201f35..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractWrapper.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.function.wrapper; - -import org.bukkit.Location; -import org.bukkit.entity.Player; - -public class InteractWrapper extends ConditionWrapper { - - private final Location location; - - public InteractWrapper(Player player, Location location) { - super(player); - this.location = location; - } - - public Location getLocation() { - return location; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceBlockWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceBlockWrapper.java deleted file mode 100644 index ab6681318..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceBlockWrapper.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.function.wrapper; - -import org.bukkit.block.Block; -import org.bukkit.entity.Player; - -public class PlaceBlockWrapper extends PlaceWrapper { - - private final Block placedBlock; - - public PlaceBlockWrapper(Player player, Block placedBlock, String blockID) { - super(player, placedBlock.getLocation(), blockID); - this.placedBlock = placedBlock; - } - - public Block getPlacedBlock() { - return placedBlock; - } -} - diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceWrapper.java deleted file mode 100644 index 668f31d6d..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceWrapper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.function.wrapper; - -import org.bukkit.Location; -import org.bukkit.entity.Player; - -public class PlaceWrapper extends ConditionWrapper { - - private final Location location; - private final String id; - - public PlaceWrapper(Player player, Location location, String id) { - super(player); - this.location = location; - this.id = id; - } - - public Location getLocation() { - return location; - } - - public String getId() { - return id; - } -} - diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/AbstractFertilizer.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/AbstractFertilizer.java deleted file mode 100644 index c7e640275..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/AbstractFertilizer.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl; - -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.item.FertilizerType; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.AbstractEventItem; - -import java.util.HashMap; -import java.util.HashSet; - -public class AbstractFertilizer extends AbstractEventItem implements Fertilizer { - - private final String key; - private final String itemID; - private final int times; - private final FertilizerType fertilizerType; - private final HashSet potWhitelist; - private final boolean beforePlant; - private final String icon; - private final Requirement[] requirements; - - public AbstractFertilizer( - String key, - String itemID, - int times, - FertilizerType fertilizerType, - HashSet potWhitelist, - boolean beforePlant, - String icon, - Requirement[] requirements, - HashMap actionMap - ) { - super(actionMap); - this.key = key; - this.itemID = itemID; - this.times = times; - this.fertilizerType = fertilizerType; - this.potWhitelist = potWhitelist; - this.beforePlant = beforePlant; - this.icon = icon; - this.requirements = requirements; - } - - @Override - public String getKey() { - return key; - } - - @Override - public String getItemID() { - return itemID; - } - - @Override - public int getTimes() { - return times; - } - - @Override - public FertilizerType getFertilizerType() { - return fertilizerType; - } - - @Override - public HashSet getPotWhitelist() { - return potWhitelist; - } - - @Override - public boolean isBeforePlant() { - return beforePlant; - } - - @Override - public String getIcon() { - return icon; - } - - @Override - public Requirement[] getRequirements() { - return requirements; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java deleted file mode 100644 index 2618d5a02..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.condition.Conditions; -import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; -import net.momirealms.customcrops.api.mechanic.item.BoneMeal; -import net.momirealms.customcrops.api.mechanic.item.Crop; -import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.AbstractEventItem; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; - -public class CropConfig extends AbstractEventItem implements Crop { - - private final String key; - private final String seedID; - private final int maxPoints; - private final boolean rotation; - private final ItemCarrier carrier; - private final Requirement[] plantRequirements; - private final Requirement[] breakRequirements; - private final Requirement[] interactRequirements; - private final BoneMeal[] boneMeals; - private final Conditions growConditions; - private final HashSet whitelistPots; - private final DeathConditions[] deathConditions; - private final HashMap point2StageConfigMap; - private final HashMap item2StageConfigMap; - - public CropConfig( - String key, - String seedID, - ItemCarrier carrier, - HashSet whitelistPots, - boolean rotation, - int maxPoints, - BoneMeal[] boneMeals, - Conditions growConditions, - DeathConditions[] deathConditions, - HashMap actionMap, - HashMap point2StageConfigMap, - Requirement[] plantRequirements, - Requirement[] breakRequirements, - Requirement[] interactRequirements - ) { - super(actionMap); - this.key = key; - this.seedID = seedID; - this.boneMeals = boneMeals; - this.rotation = rotation; - this.maxPoints = maxPoints; - this.growConditions = growConditions; - this.deathConditions = deathConditions; - this.plantRequirements = plantRequirements; - this.breakRequirements = breakRequirements; - this.interactRequirements = interactRequirements; - this.point2StageConfigMap = point2StageConfigMap; - this.whitelistPots = whitelistPots; - this.carrier = carrier; - this.item2StageConfigMap = new HashMap<>(); - for (CropStageConfig cropStageConfig : point2StageConfigMap.values()) { - if (cropStageConfig.getStageID() != null) { - this.item2StageConfigMap.put(cropStageConfig.getStageID(), cropStageConfig); - } - } - } - - @Override - public String getKey() { - return key; - } - - @Override - public String getSeedItemID() { - return seedID; - } - - @Override - public int getMaxPoints() { - return maxPoints; - } - - @Override - public Requirement[] getPlantRequirements() { - return plantRequirements; - } - - @Override - public Requirement[] getBreakRequirements() { - return breakRequirements; - } - - @Override - public Requirement[] getInteractRequirements() { - return interactRequirements; - } - - @Override - public Conditions getGrowConditions() { - return growConditions; - } - - @Override - public DeathConditions[] getDeathConditions() { - return deathConditions; - } - - @Override - public BoneMeal[] getBoneMeals() { - return boneMeals; - } - - @Override - public boolean hasRotation() { - return rotation; - } - - @Override - public Stage getStageByPoint(int point) { - return point2StageConfigMap.get(point); - } - - @NotNull - @Override - public String getStageItemByPoint(int point) { - if (point >= 0) { - Stage stage = point2StageConfigMap.get(point); - if (stage != null) { - String id = stage.getStageID(); - if (id == null) return getStageItemByPoint(point-1); - return id; - } else { - return getStageItemByPoint(point-1); - } - } - return null; - } - - @Override - public Stage getStageByItemID(String itemID) { - return item2StageConfigMap.get(itemID); - } - - @Override - public Collection getStages() { - return new ArrayList<>(point2StageConfigMap.values()); - } - - @Override - public HashSet getPotWhitelist() { - return whitelistPots; - } - - @Override - public ItemCarrier getItemCarrier() { - return carrier; - } - - public static class CropStageConfig extends AbstractEventItem implements Crop.Stage { - - @Nullable - private final String stageID; - - private final int point; - private final double hologramOffset; - private final Requirement[] interactRequirements; - private final Requirement[] breakRequirements; - - public CropStageConfig( - @Nullable String stageID, - int point, - double hologramOffset, - HashMap actionMap, - Requirement[] interactRequirements, - Requirement[] breakRequirements - ) { - super(actionMap); - this.stageID = stageID; - this.point = point; - this.hologramOffset = hologramOffset; - this.interactRequirements = interactRequirements; - this.breakRequirements = breakRequirements; - } - - @Override - public Crop getCrop() { - return CustomCropsPlugin.get().getItemManager().getCropByStageID(stageID); - } - - @Override - public double getHologramOffset() { - return hologramOffset; - } - - @Nullable - @Override - public String getStageID() { - return stageID; - } - - @Override - public int getPoint() { - return point; - } - - @Override - public Requirement[] getInteractRequirements() { - return interactRequirements; - } - - @Override - public Requirement[] getBreakRequirements() { - return breakRequirements; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java deleted file mode 100644 index ecda9369c..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl; - -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.FertilizerType; -import net.momirealms.customcrops.api.mechanic.item.Pot; -import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; -import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.AbstractEventItem; -import net.momirealms.customcrops.util.ConfigUtils; - -import java.util.HashMap; -import java.util.HashSet; - -public class PotConfig extends AbstractEventItem implements Pot { - - private final String key; - private final int storage; - private final String dryModel; - private final String wetModel; - private final boolean enableFertilizedAppearance; - private final HashMap> fertilizedPotMap; - private final PassiveFillMethod[] passiveFillMethods; - private final WaterBar waterBar; - private final Requirement[] placeRequirements; - private final Requirement[] breakRequirements; - private final Requirement[] useRequirements; - private final boolean acceptRainDrop; - private final boolean acceptNearbyWater; - private final boolean isVanillaBlock; - - public PotConfig( - String key, - int storage, - boolean acceptRainDrop, - boolean acceptNearbyWater, - String dryModel, - String wetModel, - boolean enableFertilizedAppearance, - HashMap> fertilizedPotMap, - WaterBar waterBar, - PassiveFillMethod[] passiveFillMethods, - HashMap actionMap, - Requirement[] placeRequirements, - Requirement[] breakRequirements, - Requirement[] useRequirements - ) { - super(actionMap); - this.key = key; - this.storage = storage; - this.acceptRainDrop = acceptRainDrop; - this.acceptNearbyWater = acceptNearbyWater; - this.enableFertilizedAppearance = enableFertilizedAppearance; - this.fertilizedPotMap = fertilizedPotMap; - this.passiveFillMethods = passiveFillMethods; - this.dryModel = dryModel; - this.wetModel = wetModel; - this.waterBar = waterBar; - this.placeRequirements = placeRequirements; - this.breakRequirements = breakRequirements; - this.useRequirements = useRequirements; - this.isVanillaBlock = ConfigUtils.isVanillaItem(dryModel) && ConfigUtils.isVanillaItem(wetModel); - } - - @Override - public int getStorage() { - return storage; - } - - @Override - public String getKey() { - return key; - } - - @Override - public HashSet getPotBlocks() { - HashSet set = new HashSet<>(); - set.add(wetModel); - set.add(dryModel); - for (Pair pair : fertilizedPotMap.values()) { - set.add(pair.left()); - set.add(pair.right()); - } - return set; - } - - @Override - public PassiveFillMethod[] getPassiveFillMethods() { - return passiveFillMethods; - } - - @Override - public String getDryItem() { - return dryModel; - } - - @Override - public String getWetItem() { - return wetModel; - } - - @Override - public Requirement[] getPlaceRequirements() { - return placeRequirements; - } - - @Override - public Requirement[] getBreakRequirements() { - return breakRequirements; - } - - @Override - public Requirement[] getUseRequirements() { - return useRequirements; - } - - @Override - public WaterBar getWaterBar() { - return waterBar; - } - - @Override - public boolean isRainDropAccepted() { - return acceptRainDrop; - } - - @Override - public boolean isNearbyWaterAccepted() { - return acceptNearbyWater; - } - - @Override - public String getBlockState(boolean water, FertilizerType type) { - if (type != null && enableFertilizedAppearance) { - return water ? fertilizedPotMap.get(type).right() : fertilizedPotMap.get(type).left(); - } else { - return water ? wetModel : dryModel; - } - } - - @Override - public boolean isVanillaBlock() { - return isVanillaBlock; - } - - @Override - public boolean isWetPot(String id) { - if (id.equals(getWetItem())) { - return true; - } - for (Pair pair : fertilizedPotMap.values()) { - if (pair.right().equals(id)) { - return true; - } - } - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/SprinklerConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/SprinklerConfig.java deleted file mode 100644 index 0470f3b90..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/SprinklerConfig.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl; - -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; -import net.momirealms.customcrops.api.mechanic.item.Sprinkler; -import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; -import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.AbstractEventItem; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.HashSet; - -public class SprinklerConfig extends AbstractEventItem implements Sprinkler { - - private final String key; - private final int range; - private final int storage; - private final int water; - private final boolean infinite; - private final String twoDItem; - private final String threeDItem; - private final String threeDItemWithWater; - private final WaterBar waterBar; - private final HashSet potWhitelist; - private final ItemCarrier itemCarrier; - private final PassiveFillMethod[] passiveFillMethods; - private final Requirement[] placeRequirements; - private final Requirement[] breakRequirements; - private final Requirement[] useRequirements; - - public SprinklerConfig( - String key, - ItemCarrier itemCarrier, - String twoDItem, - String threeDItem, - String threeDItemWithWater, - int range, - int storage, - int water, - boolean infinite, - WaterBar waterBar, - HashSet potWhitelist, - PassiveFillMethod[] passiveFillMethods, - HashMap actionMap, - Requirement[] placeRequirements, - Requirement[] breakRequirements, - Requirement[] useRequirements - ) { - super(actionMap); - this.key = key; - this.itemCarrier = itemCarrier; - this.twoDItem = twoDItem; - this.threeDItem = threeDItem; - this.threeDItemWithWater = threeDItemWithWater; - this.range = range; - this.storage = storage; - this.infinite = infinite; - this.water = water; - this.waterBar = waterBar; - this.potWhitelist = potWhitelist; - this.passiveFillMethods = passiveFillMethods; - this.placeRequirements = placeRequirements; - this.breakRequirements = breakRequirements; - this.useRequirements = useRequirements; - } - - @Nullable - @Override - public String get2DItemID() { - return twoDItem; - } - - @NotNull - @Override - public String get3DItemID() { - return threeDItem; - } - - @Override - @Nullable - public String get3DItemWithWater() { - return threeDItemWithWater; - } - - @Override - public int getStorage() { - return storage; - } - - @Override - public int getRange() { - return range; - } - - @Override - public String getKey() { - return key; - } - - @Override - public boolean isInfinite() { - return infinite; - } - - @Override - public int getWater() { - return water; - } - - @Override - public HashSet getPotWhitelist() { - return potWhitelist; - } - - @Override - public ItemCarrier getItemCarrier() { - return itemCarrier; - } - - @Override - public PassiveFillMethod[] getPassiveFillMethods() { - return passiveFillMethods; - } - - @Override - public Requirement[] getPlaceRequirements() { - return placeRequirements; - } - - @Override - public Requirement[] getBreakRequirements() { - return breakRequirements; - } - - @Override - public Requirement[] getUseRequirements() { - return useRequirements; - } - - @Override - public WaterBar getWaterBar() { - return waterBar; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java deleted file mode 100644 index 537f05f88..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ScoreComponent; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customcrops.api.manager.AdventureManager; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.PlaceholderManager; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.WateringCan; -import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; -import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.AbstractEventItem; -import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; -import net.momirealms.customcrops.mechanic.item.factory.Item; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -public class WateringCanConfig extends AbstractEventItem implements WateringCan { - - private final String key; - private final String itemID; - private final boolean infinite; - private final int width; - private final int length; - private final int storage; - private final int water; - private final HashSet potWhitelist; - private final HashSet sprinklerWhitelist; - private final boolean hasDynamicLore; - private final List lore; - private final PositiveFillMethod[] positiveFillMethods; - private final HashMap appearanceMap; - private final Requirement[] requirements; - private final WaterBar waterBar; - - public WateringCanConfig( - String key, - String itemID, - boolean infinite, - int width, - int length, - int storage, - int water, - boolean hasDynamicLore, - List lore, - HashSet potWhitelist, - HashSet sprinklerWhitelist, - PositiveFillMethod[] positiveFillMethods, - HashMap appearanceMap, - Requirement[] requirements, - HashMap actionMap, - WaterBar waterBar - ) { - super(actionMap); - this.itemID = itemID; - this.width = width; - this.length = length; - this.storage = storage; - this.hasDynamicLore = hasDynamicLore; - this.lore = lore; - this.potWhitelist = potWhitelist; - this.sprinklerWhitelist = sprinklerWhitelist; - this.positiveFillMethods = positiveFillMethods; - this.appearanceMap = appearanceMap; - this.requirements = requirements; - this.waterBar = waterBar; - this.key = key; - this.infinite = infinite; - this.water = water; - } - - @Override - public String getItemID() { - return itemID; - } - - @Override - public int getWidth() { - return width; - } - - @Override - public int getLength() { - return length; - } - - @Override - public int getStorage() { - return storage; - } - - @Override - public int getWater() { - return water; - } - - @Override - public boolean hasDynamicLore() { - return hasDynamicLore; - } - - @NotNull - public PositiveFillMethod[] getPositiveFillMethods() { - return positiveFillMethods; - } - - @Override - public void updateItem(Player player, ItemStack itemStack, int water, Map args) { - Item item = BukkitItemFactory.getInstance().wrap(itemStack); - int maxDurability = item.maxDamage().orElse((int) itemStack.getType().getMaxDurability()); - - if (isInfinite()) water = storage; - item.setTag(water, "WaterAmount"); - if (maxDurability != 0) { - item.setTag((int) (maxDurability * (((double) storage - water) / storage)), "Damage"); - } - if (appearanceMap.containsKey(water)) { - item.customModelData(appearanceMap.get(water)); - } - if (hasDynamicLore()) { - List lore = new ArrayList<>(item.lore().orElse(List.of())); - if (ConfigManager.protectLore()) { - lore.removeIf(line -> { - Component component = GsonComponentSerializer.gson().deserialize(line); - return component instanceof ScoreComponent scoreComponent - && scoreComponent.objective().equals("water") - && scoreComponent.name().equals("cc"); - }); - } else { - lore.clear(); - } - for (String newLore : getLore()) { - ScoreComponent.Builder builder = Component.score().name("cc").objective("water"); - builder.append(AdventureManager.getInstance().getComponentFromMiniMessage( - PlaceholderManager.getInstance().parse(player, newLore, args) - )); - lore.add(GsonComponentSerializer.gson().serialize(builder.build())); - } - item.lore(lore); - } - itemStack.setItemMeta(item.loadCopy().getItemMeta()); - } - - @Override - public int getCurrentWater(ItemStack itemStack) { - if (itemStack == null || itemStack.getType() == Material.AIR) - return 0; - Item item = BukkitItemFactory.getInstance().wrap(itemStack); - return (int) item.getTag("WaterAmount").orElse(0); - } - - @Override - public HashSet getPotWhitelist() { - return potWhitelist; - } - - @Override - public HashSet getSprinklerWhitelist() { - return sprinklerWhitelist; - } - - @Override - public String getKey() { - return key; - } - - @Override - public List getLore() { - return lore; - } - - @Override - @Nullable - public WaterBar getWaterBar() { - return waterBar; - } - - @Override - public Requirement[] getRequirements() { - return requirements; - } - - @Override - public boolean isInfinite() { - return infinite; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/QualityCropConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/QualityCropConfig.java deleted file mode 100644 index 93b18f313..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/QualityCropConfig.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl.fertilizer; - -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.FertilizerType; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.QualityCrop; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; - -import java.util.HashMap; -import java.util.HashSet; - -public class QualityCropConfig extends AbstractFertilizer implements QualityCrop { - - private final double[] ratio; - private final double chance; - - public QualityCropConfig( - String key, - String itemID, - int times, - double chance, - FertilizerType fertilizerType, - HashSet potWhitelist, - boolean beforePlant, - String icon, - Requirement[] requirements, - double[] ratio, - HashMap events - ) { - super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); - this.ratio = ratio; - this.chance = chance; - } - - @Override - public double getChance() { - return chance; - } - - @Override - public double[] getRatio() { - return ratio; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SoilRetainConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SoilRetainConfig.java deleted file mode 100644 index 0f1f48244..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SoilRetainConfig.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl.fertilizer; - -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.FertilizerType; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.SoilRetain; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; - -import java.util.HashMap; -import java.util.HashSet; - -public class SoilRetainConfig extends AbstractFertilizer implements SoilRetain { - - private final double chance; - - public SoilRetainConfig( - String key, - String itemID, - int times, - double chance, - FertilizerType fertilizerType, - HashSet potWhitelist, - boolean beforePlant, - String icon, - Requirement[] requirements, - HashMap events) { - super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); - this.chance = chance; - } - - @Override - public double getChance() { - return chance; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SpeedGrowConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SpeedGrowConfig.java deleted file mode 100644 index b211a80eb..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SpeedGrowConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl.fertilizer; - -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.FertilizerType; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.SpeedGrow; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -public class SpeedGrowConfig extends AbstractFertilizer implements SpeedGrow { - - private final List> pairs; - - public SpeedGrowConfig( - String key, - String itemID, - int times, - FertilizerType fertilizerType, - HashSet potWhitelist, - boolean beforePlant, - String icon, - Requirement[] requirements, - List> pairs, - HashMap events) { - super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); - this.pairs = pairs; - } - - @Override - public int getPointBonus() { - for (Pair pair : pairs) { - if (Math.random() < pair.left()) { - return pair.right(); - } - } - return 0; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/VariationConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/VariationConfig.java deleted file mode 100644 index 1323d234a..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/VariationConfig.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl.fertilizer; - -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.FertilizerType; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.Variation; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; - -import java.util.HashMap; -import java.util.HashSet; - -public class VariationConfig extends AbstractFertilizer implements Variation { - - private final double chanceBonus; - - public VariationConfig( - String key, - String itemID, - int times, - double chance, - FertilizerType fertilizerType, - HashSet potWhitelist, - boolean beforePlant, - String icon, - Requirement[] requirements, - HashMap events) { - super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); - this.chanceBonus = chance; - } - - @Override - public double getChanceBonus() { - return chanceBonus; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/YieldIncreaseConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/YieldIncreaseConfig.java deleted file mode 100644 index f47af9eb6..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/YieldIncreaseConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.item.impl.fertilizer; - -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.FertilizerType; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.YieldIncrease; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -public class YieldIncreaseConfig extends AbstractFertilizer implements YieldIncrease { - - private final List> pairs; - - public YieldIncreaseConfig( - String key, - String itemID, - int times, - FertilizerType fertilizerType, - HashSet potWhitelist, - boolean beforePlant, - String icon, - Requirement[] requirements, - List> pairs, - HashMap events) { - super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); - this.pairs = pairs; - } - - @Override - public int getAmountBonus() { - for (Pair pair : pairs) { - if (Math.random() < pair.left()) { - return pair.right(); - } - } - return 0; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java deleted file mode 100644 index 72b1b1955..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.misc; - -import com.comphenix.protocol.events.PacketContainer; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.scheduler.CancellableTask; -import net.momirealms.customcrops.manager.PacketManager; -import net.momirealms.customcrops.util.FakeEntityUtils; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.util.Vector; - -import java.util.ArrayList; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -public class CrowAttackAnimation { - - private CancellableTask task; - private final Location fromLocation; - private final Vector vectorDown; - private final Vector vectorUp; - private final Player[] viewers; - private int timer; - private final ItemStack flyModel; - private final ItemStack standModel; - private final int entityID = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); - private final float yaw = ThreadLocalRandom.current().nextInt(361) - 180; - - public CrowAttackAnimation(SimpleLocation cropSimpleLocation, String flyModel, String standModel) { - ArrayList viewers = new ArrayList<>(); - for (Player player : Bukkit.getOnlinePlayers()) { - if (cropSimpleLocation.isNear(SimpleLocation.of(player.getLocation()), 48)) { - viewers.add(player); - } - } - this.viewers = viewers.toArray(new Player[0]); - Location cropLocation = cropSimpleLocation.getBukkitLocation().add(0.25 + Math.random() * 0.5, 0, 0.25 + Math.random() * 0.5); - this.flyModel = CustomCropsPlugin.get().getItemManager().getItemStack(null, flyModel); - this.standModel = CustomCropsPlugin.get().getItemManager().getItemStack(null, standModel); - this.fromLocation = cropLocation.clone().add((10 * Math.sin((Math.PI * yaw)/180)), 10, (- 10 * Math.cos((Math.PI * yaw)/180))); - Location relative = cropLocation.clone().subtract(fromLocation); - this.vectorDown = new Vector(relative.getX() / 100, -0.1, relative.getZ() / 100); - this.vectorUp = new Vector(relative.getX() / 100, 0.1, relative.getZ() / 100); - } - - public void start() { - if (this.viewers.length == 0) return; - sendPacketToViewers( - FakeEntityUtils.getSpawnPacket(entityID, fromLocation, EntityType.ARMOR_STAND), - FakeEntityUtils.getVanishArmorStandMetaPacket(entityID), - FakeEntityUtils.getEquipPacket(entityID, flyModel) - ); - this.task = CustomCropsPlugin.get().getScheduler().runTaskAsyncTimer(() -> { - timer++; - if (timer < 100) { - sendPacketToViewers(FakeEntityUtils.getTeleportPacket(entityID, fromLocation.add(vectorDown), yaw)); - } else if (timer == 100){ - sendPacketToViewers(FakeEntityUtils.getEquipPacket(entityID, standModel)); - } else if (timer == 150) { - sendPacketToViewers(FakeEntityUtils.getEquipPacket(entityID, flyModel)); - } else if (timer > 150) { - sendPacketToViewers(FakeEntityUtils.getTeleportPacket(entityID, fromLocation.add(vectorUp), yaw)); - } - if (timer > 300) { - sendPacketToViewers(FakeEntityUtils.getDestroyPacket(entityID)); - task.cancel(); - } - }, 50, 50, TimeUnit.MILLISECONDS); - } - - private void sendPacketToViewers(PacketContainer... packet) { - for (Player viewer : viewers) { - PacketManager.getInstance().send(viewer, packet); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java deleted file mode 100644 index b5dbd417e..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.misc; - -import com.comphenix.protocol.events.PacketContainer; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.manager.PacketManager; -import net.momirealms.customcrops.util.FakeEntityUtils; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -public class TempFakeItem { - - private final Player[] viewers; - private final int duration; - private final int entityID = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); - private final Location location; - private final String item; - - public TempFakeItem(Location location, String item, int duration, Player viewer) { - SimpleLocation simpleLocation = SimpleLocation.of(location); - ArrayList viewers = new ArrayList<>(); - if (viewer == null) - for (Player player : location.getWorld().getPlayers()) { - if (simpleLocation.isNear(SimpleLocation.of(player.getLocation()), 48)) { - viewers.add(player); - } - } - else { - viewers.add(viewer); - } - this.viewers = viewers.toArray(new Player[0]); - this.location = location; - this.item = item; - this.duration = duration; - } - - public void start() { - if (this.viewers.length == 0) return; - sendPacketToViewers( - FakeEntityUtils.getSpawnPacket(entityID, location, EntityType.ARMOR_STAND), - FakeEntityUtils.getVanishArmorStandMetaPacket(entityID), - FakeEntityUtils.getEquipPacket(entityID, CustomCropsPlugin.get().getItemManager().getItemStack(null, item)) - ); - CustomCropsPlugin.get().getScheduler().runTaskAsyncLater(() -> { - sendPacketToViewers(FakeEntityUtils.getDestroyPacket(entityID)); - }, duration * 50L, TimeUnit.MILLISECONDS); - } - - private void sendPacketToViewers(PacketContainer... packet) { - for (Player viewer : viewers) { - PacketManager.getInstance().send(viewer, packet); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/migrator/Migration.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/migrator/Migration.java deleted file mode 100644 index dac5ecb2e..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/migrator/Migration.java +++ /dev/null @@ -1,472 +0,0 @@ -package net.momirealms.customcrops.mechanic.misc.migrator; - -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; -import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; -import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; -import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; -import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.mechanic.world.level.WorldSetting; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.world.CWorld; -import net.momirealms.customcrops.mechanic.world.adaptor.BukkitWorldAdaptor; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.Objects; - -public class Migration { - - public static void tryUpdating() { - File configFile = new File(CustomCropsPlugin.getInstance().getDataFolder(), "config.yml"); - // If not config file found, do nothing - if (!configFile.exists()) return; - - YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); - String version = config.getString("config-version"); - if (version == null) return; - - int versionNumber = Integer.parseInt(version); - if (versionNumber >= 25 && versionNumber <= 34) { - doV33Migration(config); - return; - } - if (versionNumber == 35) { - doV343Migration(); - } - } - - private static void doV343Migration() { - if (CustomCropsPlugin.get().getWorldManager().getWorldAdaptor() instanceof BukkitWorldAdaptor adaptor) { - for (World world : Bukkit.getWorlds()) { - CWorld temp = new CWorld(CustomCropsPlugin.getInstance().getWorldManager(), world); - temp.setWorldSetting(WorldSetting.of(false,300,true, 1,true,2,true,2,false,1200, false,false,28,-1,-1,-1, 0)); - adaptor.convertWorldFromV342toV343(temp, world); - } - } - } - - private static void doV33Migration(YamlConfiguration config) { - // do migration - if (config.contains("mechanics.season.sync-season")) { - config.set("mechanics.sync-season.enable", config.getBoolean("mechanics.season.sync-season.enable")); - config.set("mechanics.sync-season.reference", config.getString("mechanics.season.sync-season.reference")); - } - if (config.contains("mechanics.season.greenhouse")) { - config.set("mechanics.greenhouse.enable", config.getBoolean("mechanics.season.greenhouse.enable")); - config.set("mechanics.greenhouse.id", config.getString("mechanics.season.greenhouse.block")); - config.set("mechanics.greenhouse.range", config.getInt("mechanics.season.greenhouse.range")); - } - if (config.contains("mechanics.scarecrow")) { - config.set("mechanics.scarecrow.id", config.getString("mechanics.scarecrow")); - } - - try { - config.save(new File(CustomCropsPlugin.getInstance().getDataFolder(), "config.yml")); - } catch (IOException e) { - e.printStackTrace(); - } - - try { - YamlDocument.create( - new File(CustomCropsPlugin.getInstance().getDataFolder(), "config.yml"), - Objects.requireNonNull(CustomCropsPlugin.getInstance().getResource("config.yml")), - GeneralSettings.DEFAULT, - LoaderSettings - .builder() - .setAutoUpdate(true) - .build(), - DumperSettings.DEFAULT, - UpdaterSettings - .builder() - .setVersioning(new BasicVersioning("config-version")) - .build() - ); - } catch (IOException e) { - LogUtils.warn(e.getMessage()); - } - - updateWateringCans(); - updatePots(); - updateFertilizers(); - updateSprinklers(); - updateCrops(); - - if (CustomCropsPlugin.get().getWorldManager().getWorldAdaptor() instanceof BukkitWorldAdaptor adaptor) { - for (World world : Bukkit.getWorlds()) { - CWorld temp = new CWorld(CustomCropsPlugin.getInstance().getWorldManager(), world); - temp.setWorldSetting(WorldSetting.of(false,300,true, 1,true,2,true,2,false, 1200, false,false,28,-1,-1,-1, 0)); - adaptor.convertWorldFromV33toV34(temp, world); - } - } - } - - private static void updateWateringCans() { - var files = ConfigUtils.getFilesRecursively(new File(CustomCropsPlugin.getInstance().getDataFolder(), "contents" + File.separator + "watering-cans")); - for (File file : files) { - YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); - for (Map.Entry sections : yaml.getValues(false).entrySet()) { - if (sections.getValue() instanceof ConfigurationSection section) { - ConfigurationSection fillSection = section.getConfigurationSection("fill-method"); - if (fillSection != null) { - for (String key : fillSection.getKeys(false)) { - fillSection.set(key + ".particle", null); - fillSection.set(key + ".sound", null); - } - } - if (section.contains("sound")) { - section.set("events.consume_water.sound_action.type", "sound"); - section.set("events.consume_water.sound_action.value.key", section.getString("sound")); - section.set("events.consume_water.sound_action.value.source", "player"); - section.set("events.consume_water.sound_action.value.volume", 1); - section.set("events.consume_water.sound_action.value.pitch", 1); - section.set("sound", null); - } - if (section.contains("particle")) { - section.set("events.add_water.particle_action.type", "particle"); - section.set("events.add_water.particle_action.value.particle", section.getString("particle")); - section.set("events.add_water.particle_action.value.x", 0.5); - section.set("events.add_water.particle_action.value.z", 0.5); - section.set("events.add_water.particle_action.value.y", 1.3); - section.set("events.add_water.particle_action.value.count", 5); - section.set("particle", null); - } - if (section.contains("actionbar")) { - if (section.getBoolean("actionbar.enable")) { - section.set("events.consume_water.actionbar_action.type", "actionbar"); - section.set("events.consume_water.actionbar_action.value", section.getString("actionbar.content")); - section.set("events.add_water.actionbar_action.type", "actionbar"); - section.set("events.add_water.actionbar_action.value", section.getString("actionbar.content")); - } - section.set("actionbar", null); - } - section.set("events.add_water.sound_action.type", "sound"); - section.set("events.add_water.sound_action.value.key", "minecraft:item.bucket.empty"); - section.set("events.add_water.sound_action.value.source", "player"); - section.set("events.add_water.sound_action.value.volume", 1); - section.set("events.add_water.sound_action.value.pitch", 1); - } - } - try { - yaml.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private static void updatePots() { - var files = ConfigUtils.getFilesRecursively(new File(CustomCropsPlugin.getInstance().getDataFolder(), "contents" + File.separator + "pots")); - for (File file : files) { - YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); - for (Map.Entry sections : yaml.getValues(false).entrySet()) { - if (sections.getValue() instanceof ConfigurationSection section) { - section.set("absorb-rainwater", true); - section.set("absorb-nearby-water", false); - ConfigurationSection fillSection = section.getConfigurationSection("fill-method"); - if (fillSection != null) { - for (String key : fillSection.getKeys(false)) { - fillSection.set(key + ".particle", null); - fillSection.set(key + ".sound", null); - } - } - if (section.contains("hologram.water.water-bar")) { - section.set("water-bar.left", section.getString("hologram.water.water-bar.left")); - section.set("water-bar.right", section.getString("hologram.water.water-bar.right")); - section.set("water-bar.empty", section.getString("hologram.water.water-bar.empty")); - section.set("water-bar.full", section.getString("hologram.water.water-bar.full")); - } - section.set("events.add_water.particle_action.type", "particle"); - section.set("events.add_water.particle_action.value.particle", "WATER_SPLASH"); - section.set("events.add_water.particle_action.value.x", 0.5); - section.set("events.add_water.particle_action.value.z", 0.5); - section.set("events.add_water.particle_action.value.y", 1.3); - section.set("events.add_water.particle_action.value.count", 5); - section.set("events.add_water.particle_action.value.offset-x", 0.3); - section.set("events.add_water.particle_action.value.offset-z", 0.3); - - ConfigurationSection holoSection = section.getConfigurationSection("hologram"); - if (holoSection != null) { - int duration = holoSection.getInt("duration") * 20; - String requireItem = holoSection.getString("require-item", "*"); - section.set("events.interact.conditional_action.type", "conditional"); - section.set("events.interact.conditional_action.value.conditions.requirement_1.type", "item-in-hand"); - section.set("events.interact.conditional_action.value.conditions.requirement_1.value.amount", 1); - section.set("events.interact.conditional_action.value.conditions.requirement_1.value.item", requireItem); - if (holoSection.getBoolean("water.enable")) { - String waterText = holoSection.getString("water.content"); - section.set("events.interact.conditional_action.value.actions.water_hologram.type", "hologram"); - section.set("events.interact.conditional_action.value.actions.water_hologram.value.duration", duration); - section.set("events.interact.conditional_action.value.actions.water_hologram.value.text", waterText); - section.set("events.interact.conditional_action.value.actions.water_hologram.value.apply-correction", true); - section.set("events.interact.conditional_action.value.actions.water_hologram.value.x", 0.5); - section.set("events.interact.conditional_action.value.actions.water_hologram.value.y", 0.6); - section.set("events.interact.conditional_action.value.actions.water_hologram.value.z", 0.5); - } - if (holoSection.getBoolean("fertilizer.enable")) { - String fertilizerText = holoSection.getString("fertilizer.content"); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.type", "conditional"); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.conditions.requirement_1.type", "fertilizer"); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.conditions.requirement_1.value.has", true); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.actions.fertilizer_hologram.type", "hologram"); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.actions.fertilizer_hologram.value.text", fertilizerText); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.actions.fertilizer_hologram.value.duration", duration); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.actions.fertilizer_hologram.value.apply-correction", true); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.actions.fertilizer_hologram.value.x", 0.5); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.actions.fertilizer_hologram.value.y", 0.83); - section.set("events.interact.conditional_action.value.actions.conditional_fertilizer_action.value.actions.fertilizer_hologram.value.z", 0.5); - } - section.set("hologram", null); - } - } - } - try { - yaml.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private static void updateFertilizers() { - var files = ConfigUtils.getFilesRecursively(new File(CustomCropsPlugin.getInstance().getDataFolder(), "contents" + File.separator + "fertilizers")); - for (File file : files) { - YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); - for (Map.Entry sections : yaml.getValues(false).entrySet()) { - if (sections.getValue() instanceof ConfigurationSection section) { - if (section.contains("particle")) { - section.set("events.use.particle_action.type", "particle"); - section.set("events.use.particle_action.value.particle", section.getString("particle")); - section.set("events.use.particle_action.value.x", 0.5); - section.set("events.use.particle_action.value.y", 1.3); - section.set("events.use.particle_action.value.z", 0.5); - section.set("events.use.particle_action.value.count", 5); - section.set("events.use.particle_action.value.offset-x", 0.3); - section.set("events.use.particle_action.value.offset-z", 0.3); - section.set("particle", null); - } - if (section.contains("sound")) { - section.set("events.use.sound_action.type", "sound"); - section.set("events.use.sound_action.value.source", "player"); - section.set("events.use.sound_action.value.key", section.getString("sound")); - section.set("events.use.sound_action.value.volume", 1); - section.set("events.use.sound_action.value.pitch", 1); - section.set("sound", null); - } - if (section.contains("pot-whitelist")) { - section.set("events.wrong_pot.sound_action.type", "sound"); - section.set("events.wrong_pot.sound_action.value.source", "player"); - section.set("events.wrong_pot.sound_action.value.key", "minecraft:item.bundle.insert"); - section.set("events.wrong_pot.sound_action.value.volume", 1); - section.set("events.wrong_pot.sound_action.value.pitch", 1); - section.set("events.wrong_pot.actionbar_action.type", "actionbar"); - section.set("events.wrong_pot.actionbar_action.value", "[X] This fertilizer can only be used in pots."); - } - if (section.getBoolean("before-plant")) { - section.set("events.before_plant.sound_action.type", "sound"); - section.set("events.before_plant.sound_action.value.source", "player"); - section.set("events.before_plant.sound_action.value.key", "minecraft:item.bundle.insert"); - section.set("events.before_plant.sound_action.value.volume", 1); - section.set("events.before_plant.sound_action.value.pitch", 1); - section.set("events.before_plant.actionbar_action.type", "actionbar"); - section.set("events.before_plant.actionbar_action.value", "[X] You can only use this fertilizer before planting the crop."); - } - } - } - try { - yaml.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private static void updateSprinklers() { - var files = ConfigUtils.getFilesRecursively(new File(CustomCropsPlugin.getInstance().getDataFolder(), "contents" + File.separator + "sprinklers")); - for (File file : files) { - YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); - for (Map.Entry sections : yaml.getValues(false).entrySet()) { - if (sections.getValue() instanceof ConfigurationSection section) { - section.set("infinite", false); - ConfigurationSection fillSection = section.getConfigurationSection("fill-method"); - if (fillSection != null) { - for (String key : fillSection.getKeys(false)) { - fillSection.set(key + ".particle", null); - fillSection.set(key + ".sound", null); - } - } - if (section.contains("place-sound")) { - section.set("events.place.sound_action.type", "sound"); - section.set("events.place.sound_action.value.key", section.getString("place-sound")); - section.set("events.place.sound_action.value.source", "player"); - section.set("events.place.sound_action.value.volume", 1); - section.set("events.place.sound_action.value.pitch", 1); - section.set("place-sound", null); - } - section.set("events.interact.force_work_action.type", "conditional"); - section.set("events.interact.force_work_action.value.conditions.requirement_1.type", "sneak"); - section.set("events.interact.force_work_action.value.conditions.requirement_1.value", true); - section.set("events.interact.force_work_action.value.actions.action_1.type", "force-tick"); - if (section.contains("hologram")) { - if (section.getBoolean("hologram.enable")) { - int duration = section.getInt("hologram.duration") * 20; - String text = section.getString("hologram.content"); - if (section.contains("hologram.water-bar")) { - section.set("water-bar.left", section.getString("hologram.water-bar.left")); - section.set("water-bar.right", section.getString("hologram.water-bar.right")); - section.set("water-bar.empty", section.getString("hologram.water-bar.empty")); - section.set("water-bar.full", section.getString("hologram.water-bar.full")); - } - section.set("events.interact.hologram_action.type", "hologram"); - section.set("events.interact.hologram_action.value.duration", duration); - section.set("events.interact.hologram_action.value.text", text); - section.set("events.interact.hologram_action.value.x", 0.5); - section.set("events.interact.hologram_action.value.y", -0.3); - section.set("events.interact.hologram_action.value.z", 0.5); - section.set("events.interact.hologram_action.value.visible-to-all", false); - } - section.set("hologram", null); - } - if (section.contains("animation")) { - if (section.getBoolean("animation.enable")) { - String item = section.getString("animation.item"); - int duration = section.getInt("animation.duration") * 20; - section.set("events.work.fake_item_action.type", "fake-item"); - section.set("events.work.fake_item_action.value.item", item); - section.set("events.work.fake_item_action.value.duration", duration); - section.set("events.work.fake_item_action.value.x", 0.5); - section.set("events.work.fake_item_action.value.y", 0.4); - section.set("events.work.fake_item_action.value.z", 0.5); - section.set("events.work.fake_item_action.value.visible-to-all", true); - } - section.set("animation", null); - } - section.set("events.add_water.particle_action.type", "particle"); - section.set("events.add_water.particle_action.value.particle", "WATER_SPLASH"); - section.set("events.add_water.particle_action.value.x", 0.5); - section.set("events.add_water.particle_action.value.z", 0.5); - section.set("events.add_water.particle_action.value.y", 0.7); - section.set("events.add_water.particle_action.value.count", 5); - section.set("events.add_water.sound_action.type", "sound"); - section.set("events.add_water.sound_action.value.key", "minecraft:item.bucket.empty"); - section.set("events.add_water.sound_action.value.source", "player"); - section.set("events.add_water.sound_action.value.pitch", 1); - section.set("events.add_water.sound_action.value.volume", 1); - } - } - try { - yaml.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private static void updateCrops() { - var files = ConfigUtils.getFilesRecursively(new File(CustomCropsPlugin.getInstance().getDataFolder(), "contents" + File.separator + "crops")); - for (File file : files) { - YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); - for (Map.Entry sections : yaml.getValues(false).entrySet()) { - if (sections.getValue() instanceof ConfigurationSection section) { - if (section.contains("plant-actions")) { - section.set("events.plant", section.getConfigurationSection("plant-actions")); - section.set("plant-actions", null); - } - ConfigurationSection boneMeal = section.getConfigurationSection("custom-bone-meal"); - if (boneMeal != null) { - for (Map.Entry entry : boneMeal.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - inner.set("actions.swing_action.type", "swing-hand"); - inner.set("actions.swing_action.value", true); - if (inner.contains("particle")) { - inner.set("actions.particle_action.type", "particle"); - inner.set("actions.particle_action.value.particle", inner.getString("particle")); - inner.set("actions.particle_action.value.count",5); - inner.set("actions.particle_action.value.x", 0.5); - inner.set("actions.particle_action.value.y", 0.5); - inner.set("actions.particle_action.value.z", 0.5); - inner.set("actions.particle_action.value.offset-x", 0.3); - inner.set("actions.particle_action.value.offset-y", 0.3); - inner.set("actions.particle_action.value.offset-z", 0.3); - inner.set("particle", null); - } - if (inner.contains("sound")) { - inner.set("actions.sound_action.type", "sound"); - inner.set("actions.sound_action.value.key", inner.getString("sound")); - inner.set("actions.sound_action.value.source", "player"); - inner.set("actions.sound_action.value.volume", 1); - inner.set("actions.sound_action.value.pitch", 1); - inner.set("sound", null); - } - } - } - } - - ConfigurationSection pointSection = section.getConfigurationSection("points"); - if (pointSection == null) continue; - for (Map.Entry entry1 : pointSection.getValues(false).entrySet()) { - if (entry1.getValue() instanceof ConfigurationSection pointS1) { - ConfigurationSection eventSection = pointS1.getConfigurationSection("events"); - if (eventSection != null) { - if (eventSection.contains("interact-by-hand")) { - eventSection.set("interact.empty_hand_action.type", "conditional"); - eventSection.set("interact.empty_hand_action.value.conditions", eventSection.getConfigurationSection("interact-by-hand.requirements")); - eventSection.set("interact.empty_hand_action.value.conditions.requirement_empty_hand.type", "item-in-hand"); - eventSection.set("interact.empty_hand_action.value.conditions.requirement_empty_hand.value.item", "AIR"); - eventSection.set("interact.empty_hand_action.value.actions", eventSection.getConfigurationSection("interact-by-hand")); - eventSection.set("interact.empty_hand_action.value.actions.requirements", null); - eventSection.set("interact-by-hand", null); - } - if (eventSection.contains("interact-with-item")) { - ConfigurationSection interactWithItem = eventSection.getConfigurationSection("interact-with-item"); - if (interactWithItem != null) { - int amount = 0; - for (Map.Entry entry : interactWithItem.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - amount++; - String requiredItem = inner.getString("item"); - boolean consume = inner.getBoolean("reduce-amount"); - String returned = inner.getString("return"); - ConfigurationSection actions = inner.getConfigurationSection("actions"); - eventSection.set("interact.action_" + amount + ".type", "conditional"); - eventSection.set("interact.action_" + amount + ".type", "conditional"); - eventSection.set("interact.action_" + amount + ".value.conditions", inner.getConfigurationSection("requirements")); - eventSection.set("interact.action_" + amount + ".value.conditions.requirement_item.type", "item-in-hand"); - eventSection.set("interact.action_" + amount + ".value.conditions.requirement_item.value.item", requiredItem); - eventSection.set("interact.action_" + amount + ".value.actions", actions); - if (consume) { - eventSection.set("interact.action_" + amount + ".value.actions.consume_item.type", "item-amount"); - eventSection.set("interact.action_" + amount + ".value.actions.consume_item.value", -1); - } - if (returned != null) { - eventSection.set("interact.action_" + amount + ".value.actions.return_item.type", "give-item"); - eventSection.set("interact.action_" + amount + ".value.actions.return_item.value.id", returned); - eventSection.set("interact.action_" + amount + ".value.actions.return_item.value.amount", 1); - } - } - } - } - eventSection.set("interact-with-item", null); - } - } - - } - } - - } - } - try { - yaml.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java deleted file mode 100644 index cb8d2893d..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java +++ /dev/null @@ -1,1041 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.requirement; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.integration.LevelInterface; -import net.momirealms.customcrops.api.integration.SeasonInterface; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.RequirementManager; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.requirement.RequirementExpansion; -import net.momirealms.customcrops.api.mechanic.requirement.RequirementFactory; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.compatibility.VaultHook; -import net.momirealms.customcrops.compatibility.papi.ParseUtils; -import net.momirealms.customcrops.util.ClassUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.sparrow.heart.SparrowHeart; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.MemorySection; -import org.bukkit.inventory.ItemStack; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.*; - -public class RequirementManagerImpl implements RequirementManager { - - private final CustomCropsPlugin plugin; - private final HashMap requirementBuilderMap; - private final String EXPANSION_FOLDER = "expansions/requirement"; - - public RequirementManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.requirementBuilderMap = new HashMap<>(); - this.registerInbuiltRequirements(); - } - - @Override - public void load() { - this.loadExpansions(); - } - - @Override - public void unload() { - } - - @Override - public void disable() { - this.requirementBuilderMap.clear(); - } - - @Override - public boolean registerRequirement(String type, RequirementFactory requirementFactory) { - if (this.requirementBuilderMap.containsKey(type)) return false; - this.requirementBuilderMap.put(type, requirementFactory); - return true; - } - - @Override - public boolean unregisterRequirement(String type) { - return this.requirementBuilderMap.remove(type) != null; - } - - private void registerInbuiltRequirements() { - this.registerTimeRequirement(); - this.registerYRequirement(); - this.registerContainRequirement(); - this.registerStartWithRequirement(); - this.registerEndWithRequirement(); - this.registerEqualsRequirement(); - this.registerBiomeRequirement(); - this.registerDateRequirement(); - this.registerPluginLevelRequirement(); - this.registerPermissionRequirement(); - this.registerWorldRequirement(); - this.registerWeatherRequirement(); - this.registerSeasonRequirement(); - this.registerGreaterThanRequirement(); - this.registerAndRequirement(); - this.registerOrRequirement(); - this.registerLevelRequirement(); - this.registerRandomRequirement(); - this.registerCoolDownRequirement(); - this.registerLessThanRequirement(); - this.registerNumberEqualRequirement(); - this.registerRegexRequirement(); - this.registerMoneyRequirement(); - this.registerEnvironmentRequirement(); - this.registerPotionEffectRequirement(); - this.registerInListRequirement(); - this.registerItemInHandRequirement(); - this.registerSneakRequirement(); - this.registerTemperatureRequirement(); - this.registerFertilizerRequirement(); - this.registerLightRequirement(); - this.registerGameModeRequirement(); - } - - @NotNull - @Override - public Requirement[] getRequirements(ConfigurationSection section, boolean advanced) { - List requirements = new ArrayList<>(); - if (section == null) { - return requirements.toArray(new Requirement[0]); - } - for (Map.Entry entry : section.getValues(false).entrySet()) { - String typeOrName = entry.getKey(); - if (hasRequirement(typeOrName)) { - requirements.add(getRequirement(typeOrName, entry.getValue())); - } else { - requirements.add(getRequirement(section.getConfigurationSection(typeOrName), advanced)); - } - } - return requirements.toArray(new Requirement[0]); - } - - @NotNull - @Override - public Requirement getRequirement(ConfigurationSection section, boolean advanced) { - if (section == null) return EmptyRequirement.instance; - List actionList = null; - if (advanced) { - actionList = new ArrayList<>(); - if (section.contains("not-met-actions")) { - for (Map.Entry entry : Objects.requireNonNull(section.getConfigurationSection("not-met-actions")).getValues(false).entrySet()) { - if (entry.getValue() instanceof MemorySection inner) { - actionList.add(plugin.getActionManager().getAction(inner)); - } - } - } - if (section.contains("message")) { - List messages = ConfigUtils.stringListArgs(section.get("message")); - actionList.add(plugin.getActionManager().getActionFactory("message").build(messages, 1)); - } - if (actionList.size() == 0) - actionList = null; - } - String type = section.getString("type"); - if (type == null) { - LogUtils.warn("No requirement type found at " + section.getCurrentPath()); - return EmptyRequirement.instance; - } - var builder = getRequirementFactory(type); - if (builder == null) { - return EmptyRequirement.instance; - } - return builder.build(section.get("value"), actionList, advanced); - } - - @Override - @NotNull - public Requirement getRequirement(String type, Object value) { - RequirementFactory factory = getRequirementFactory(type); - if (factory == null) { - LogUtils.warn("Requirement type: " + type + " doesn't exist."); - return EmptyRequirement.instance; - } - return factory.build(value); - } - - @Override - @Nullable - public RequirementFactory getRequirementFactory(String type) { - return requirementBuilderMap.get(type); - } - - private void registerTimeRequirement() { - registerRequirement("time", (args, actions, advanced) -> { - List> timePairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return state -> { - long time = state.getLocation().getWorld().getTime(); - for (Pair pair : timePairs) - if (time >= pair.left() && time <= pair.right()) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerYRequirement() { - registerRequirement("ypos", (args, actions, advanced) -> { - List> timePairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return state -> { - int y = state.getLocation().getBlockY(); - for (Pair pair : timePairs) - if (y >= pair.left() && y <= pair.right()) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerGameModeRequirement() { - registerRequirement("gamemode", (args, actions, advanced) -> { - List modes = ConfigUtils.stringListArgs(args); - return condition -> { - if (condition.getPlayer() == null) return true; - var name = condition.getPlayer().getGameMode().name().toLowerCase(Locale.ENGLISH); - if (modes.contains(name)) { - return true; - } - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerTemperatureRequirement() { - registerRequirement("temperature", (args, actions, advanced) -> { - List> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return state -> { - Location location = state.getLocation(); - double temp = location.getWorld().getTemperature(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - for (Pair pair : tempPairs) - if (temp >= pair.left() && temp <= pair.right()) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerLightRequirement() { - registerRequirement("light", (args, actions, advanced) -> { - List> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return state -> { - Location location = state.getLocation(); - int temp = location.getBlock().getLightLevel(); - for (Pair pair : tempPairs) - if (temp >= pair.left() && temp <= pair.right()) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - registerRequirement("natural-light", (args, actions, advanced) -> { - List> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return state -> { - Location location = state.getLocation(); - int temp = location.getBlock().getLightFromSky(); - for (Pair pair : tempPairs) - if (temp >= pair.left() && temp <= pair.right()) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerOrRequirement() { - registerRequirement("||", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - Requirement[] requirements = getRequirements(section, advanced); - return state -> { - for (Requirement requirement : requirements) { - if (requirement.isStateMet(state)) { - return true; - } - } - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at || requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerAndRequirement() { - registerRequirement("&&", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - Requirement[] requirements = getRequirements(section, advanced); - return state -> { - outer: { - for (Requirement requirement : requirements) { - if (!requirement.isStateMet(state)) { - break outer; - } - } - return true; - } - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at && requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerLevelRequirement() { - registerRequirement("level", (args, actions, advanced) -> { - int level = (int) args; - return state -> { - if (state.getPlayer() == null) return true; - int current = state.getPlayer().getLevel(); - if (current >= level) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerMoneyRequirement() { - registerRequirement("money", (args, actions, advanced) -> { - double money = ConfigUtils.getDoubleValue(args); - return state -> { - if (state.getPlayer() == null) return true; - double current = VaultHook.getEconomy().getBalance(state.getPlayer()); - if (current >= money) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerRandomRequirement() { - registerRequirement("random", (args, actions, advanced) -> { - double random = ConfigUtils.getDoubleValue(args); - return state -> { - if (Math.random() < random) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerBiomeRequirement() { - registerRequirement("biome", (args, actions, advanced) -> { - HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return state -> { - String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(state.getLocation()); - if (biomes.contains(currentBiome)) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - registerRequirement("!biome", (args, actions, advanced) -> { - HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return state -> { - String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(state.getLocation()); - if (!biomes.contains(currentBiome)) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerWorldRequirement() { - registerRequirement("world", (args, actions, advanced) -> { - HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); - return state -> { - if (worlds.contains(state.getLocation().getWorld().getName())) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - registerRequirement("!world", (args, actions, advanced) -> { - HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); - return state -> { - if (!worlds.contains(state.getLocation().getWorld().getName())) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerWeatherRequirement() { - registerRequirement("weather", (args, actions, advanced) -> { - List weathers = ConfigUtils.stringListArgs(args); - return state -> { - String currentWeather; - World world = state.getLocation().getWorld(); - if (world.isClearWeather()) currentWeather = "clear"; - else if (world.isThundering()) currentWeather = "thunder"; - else currentWeather = "rain"; - for (String weather : weathers) - if (weather.equalsIgnoreCase(currentWeather)) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerCoolDownRequirement() { - registerRequirement("cooldown", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String key = section.getString("key"); - int time = section.getInt("time"); - return state -> { - if (state.getPlayer() == null) return true; - if (!plugin.getCoolDownManager().isCoolDown(state.getPlayer().getUniqueId(), key, time)) { - return true; - } - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at cooldown requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerDateRequirement() { - registerRequirement("date", (args, actions, advanced) -> { - HashSet dates = new HashSet<>(ConfigUtils.stringListArgs(args)); - return state -> { - Calendar calendar = Calendar.getInstance(); - String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE); - if (dates.contains(current)) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerSneakRequirement() { - registerRequirement("sneak", (args, actions, advanced) -> { - boolean sneak = (boolean) args; - return state -> { - if (state.getPlayer() == null) return true; - if (sneak) { - if (state.getPlayer().isSneaking()) - return true; - } else { - if (!state.getPlayer().isSneaking()) - return true; - } - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerPermissionRequirement() { - registerRequirement("permission", (args, actions, advanced) -> { - List perms = ConfigUtils.stringListArgs(args); - return state -> { - if (state.getPlayer() == null) return true; - for (String perm : perms) - if (state.getPlayer().hasPermission(perm)) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - registerRequirement("!permission", (args, actions, advanced) -> { - List perms = ConfigUtils.stringListArgs(args); - return state -> { - if (state.getPlayer() == null) return true; - for (String perm : perms) - if (state.getPlayer().hasPermission(perm)) { - if (advanced) triggerActions(actions, state); - return false; - } - return true; - }; - }); - } - - private void registerSeasonRequirement() { - registerRequirement("season", (args, actions, advanced) -> { - HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(str -> str.toUpperCase(Locale.ENGLISH)).toList()); - return state -> { - Location location = state.getLocation(); - SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface(); - if (seasonInterface == null) return true; - Season season = seasonInterface.getSeason(location.getWorld()); - if (season == null) return true; - if (seasons.contains(season.name())) return true; - if (ConfigManager.enableGreenhouse()) { - for (int i = 1; i <= ConfigManager.greenhouseRange(); i++) { - if (plugin.getWorldManager().getGlassAt(SimpleLocation.of(location.clone().add(0,i,0))).isPresent()) { - return true; - } - } - } - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - @SuppressWarnings("DuplicatedCode") - private void registerGreaterThanRequirement() { - registerRequirement(">=", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) >= Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at >= requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement(">", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) > Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at > requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerRegexRequirement() { - registerRequirement("regex", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("papi", ""); - String v2 = section.getString("regex", ""); - return state -> { - if (ParseUtils.setPlaceholders(state.getPlayer(), v1).matches(v2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at regex requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerNumberEqualRequirement() { - registerRequirement("=", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) == Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at = requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("==", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) == Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at == requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!=", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) != Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at != requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerFertilizerRequirement() { - registerRequirement("fertilizer", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - boolean has = section.getBoolean("has"); - HashSet keys = new HashSet<>(ConfigUtils.stringListArgs(section.get("key"))); - int y = section.getInt("y", 0); - return condition -> { - Location location = condition.getLocation().clone().add(0,y,0); - SimpleLocation simpleLocation = SimpleLocation.of(location); - Optional optionalCustomCropsBlock = plugin.getWorldManager().getBlockAt(simpleLocation); - if (optionalCustomCropsBlock.isPresent()) { - if (optionalCustomCropsBlock.get() instanceof WorldPot pot) { - Fertilizer fertilizer = pot.getFertilizer(); - if (fertilizer == null) { - if (!has && keys.size() == 0) { - return true; - } - } else { - String key = fertilizer.getKey(); - if (has) { - if (keys.size() == 0) { - return true; - } - if (keys.contains(key)) { - return true; - } - } else { - if (!keys.contains(key)) { - return true; - } - } - } - } - } - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at fertilizer requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerItemInHandRequirement() { - registerRequirement("item-in-hand", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - int amount = section.getInt("amount", 0); - List items = ConfigUtils.stringListArgs(section.get("item")); - return condition -> { - ItemStack itemStack = condition.getItemInHand(); - if (itemStack == null) itemStack = new ItemStack(Material.AIR); - String id; - if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) { - id = "AIR"; - } else { - id = plugin.getItemManager().getItemID(itemStack); - } - if ((items.contains(id) || items.contains("*")) && itemStack.getAmount() >= amount) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at item-in-hand requirement."); - return EmptyRequirement.instance; - } - }); - } - - @SuppressWarnings("DuplicatedCode") - private void registerLessThanRequirement() { - registerRequirement("<", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) < Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at < requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("<=", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) <= Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at <= requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerStartWithRequirement() { - registerRequirement("startsWith", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (p1.startsWith(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at startsWith requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!startsWith", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (!p1.startsWith(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerEndWithRequirement() { - registerRequirement("endsWith", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (p1.endsWith(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at endsWith requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!endsWith", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (!p1.endsWith(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !endsWith requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerContainRequirement() { - registerRequirement("contains", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (p1.contains(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at contains requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!contains", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (!p1.contains(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !contains requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerInListRequirement() { - registerRequirement("in-list", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String papi = section.getString("papi", ""); - HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); - return state -> { - String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), papi) : papi; - if (values.contains(p1)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at in-list requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!in-list", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String papi = section.getString("papi", ""); - HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); - return state -> { - String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), papi) : papi; - if (!values.contains(p1)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !in-list requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerEqualsRequirement() { - registerRequirement("equals", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (p1.equals(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at equals requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!equals", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return state -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; - if (!p1.equals(p2)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !equals requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerEnvironmentRequirement() { - registerRequirement("environment", (args, actions, advanced) -> { - List environments = ConfigUtils.stringListArgs(args); - return state -> { - var name = state.getLocation().getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); - if (environments.contains(name)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - registerRequirement("!environment", (args, actions, advanced) -> { - List environments = ConfigUtils.stringListArgs(args); - return state -> { - var name = state.getLocation().getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); - if (!environments.contains(name)) return true; - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void registerPluginLevelRequirement() { - registerRequirement("plugin-level", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String pluginName = section.getString("plugin"); - int level = section.getInt("level"); - String target = section.getString("target"); - return state -> { - LevelInterface levelInterface = plugin.getIntegrationManager().getLevelPlugin(pluginName); - if (levelInterface == null) { - LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation."); - return true; - } - if (levelInterface.getLevel(state.getPlayer(), target) >= level) - return true; - if (advanced) triggerActions(actions, state); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at plugin-level requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerPotionEffectRequirement() { - registerRequirement("potion-effect", (args, actions, advanced) -> { - String potions = (String) args; - String[] split = potions.split("(<=|>=|<|>|==|=)", 2); - PotionEffectType type = PotionEffectType.getByName(split[0]); - if (type == null) { - LogUtils.warn("Potion effect doesn't exist: " + split[0]); - return EmptyRequirement.instance; - } - int required = Integer.parseInt(split[1]); - String operator = potions.substring(split[0].length(), potions.length() - split[1].length()); - return state -> { - if (state.getPlayer() == null) return true; - int level = -1; - PotionEffect potionEffect = state.getPlayer().getPotionEffect(type); - if (potionEffect != null) { - level = potionEffect.getAmplifier(); - } - boolean result = false; - switch (operator) { - case ">=" -> { - if (level >= required) result = true; - } - case ">" -> { - if (level > required) result = true; - } - case "==", "=" -> { - if (level == required) result = true; - } - case "!=" -> { - if (level != required) result = true; - } - case "<=" -> { - if (level <= required) result = true; - } - case "<" -> { - if (level < required) result = true; - } - } - if (result) { - return true; - } - if (advanced) triggerActions(actions, state); - return false; - }; - }); - } - - private void triggerActions(List actions, State state) { - if (actions != null) - for (Action action : actions) - action.trigger(state); - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private void loadExpansions() { - File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); - if (!expansionFolder.exists()) - expansionFolder.mkdirs(); - - List> classes = new ArrayList<>(); - File[] expansionJars = expansionFolder.listFiles(); - if (expansionJars == null) return; - for (File expansionJar : expansionJars) { - if (expansionJar.getName().endsWith(".jar")) { - try { - Class expansionClass = ClassUtils.findClass(expansionJar, RequirementExpansion.class); - classes.add(expansionClass); - } catch (IOException | ClassNotFoundException e) { - LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); - } - } - } - try { - for (Class expansionClass : classes) { - RequirementExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); - unregisterRequirement(expansion.getRequirementType()); - registerRequirement(expansion.getRequirementType(), expansion.getRequirementFactory()); - LogUtils.info("Loaded requirement expansion: " + expansion.getRequirementType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor()); - } - } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { - LogUtils.warn("Error occurred when creating expansion instance.", e); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java deleted file mode 100644 index a3cf27717..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java +++ /dev/null @@ -1,624 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.Crop; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.item.Pot; -import net.momirealms.customcrops.api.mechanic.item.Sprinkler; -import net.momirealms.customcrops.api.mechanic.misc.CRotation; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import net.momirealms.customcrops.api.mechanic.world.BlockPos; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.*; -import net.momirealms.customcrops.mechanic.world.block.MemoryPot; -import net.momirealms.customcrops.mechanic.world.block.MemorySprinkler; -import net.momirealms.customcrops.scheduler.task.TickTask; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadLocalRandom; - -public class CChunk implements CustomCropsChunk { - - private transient CWorld cWorld; - private final ChunkPos chunkPos; - private final ConcurrentHashMap loadedSections; - private final PriorityQueue queue; - private final Set tickedBlocks; - private long lastLoadedTime; - private int loadedSeconds; - private int unloadedSeconds; - private boolean notified; - - public CChunk(CWorld cWorld, ChunkPos chunkPos) { - this.cWorld = cWorld; - this.chunkPos = chunkPos; - this.loadedSections = new ConcurrentHashMap<>(64); - this.queue = new PriorityQueue<>(); - this.unloadedSeconds = 0; - this.tickedBlocks = Collections.synchronizedSet(new HashSet<>()); - this.updateLastLoadedTime(); - this.notified = true; - } - - public CChunk( - CWorld cWorld, - ChunkPos chunkPos, - int loadedSeconds, - long lastLoadedTime, - ConcurrentHashMap loadedSections, - PriorityQueue queue, - HashSet tickedBlocks - ) { - this.cWorld = cWorld; - this.chunkPos = chunkPos; - this.loadedSections = loadedSections; - this.lastLoadedTime = lastLoadedTime; - this.loadedSeconds = loadedSeconds; - this.queue = queue; - this.unloadedSeconds = 0; - this.tickedBlocks = Collections.synchronizedSet(tickedBlocks); - } - - @Override - public void updateLastLoadedTime() { - this.lastLoadedTime = System.currentTimeMillis(); - } - - @Override - public void notifyOfflineUpdates() { - this.notified = true; - long current = System.currentTimeMillis(); - int offlineTimeInSeconds = (int) (current - this.lastLoadedTime) / 1000; - CustomCropsPlugin.get().debug(chunkPos.toString() + " Offline seconds: " + offlineTimeInSeconds + "s."); - offlineTimeInSeconds = Math.min(offlineTimeInSeconds, cWorld.getWorldSetting().getMaxOfflineTime()); - this.lastLoadedTime = current; - var setting = cWorld.getWorldSetting(); - int minTickUnit = setting.getMinTickUnit(); - for (int i = 0; i < offlineTimeInSeconds; i++) { - this.loadedSeconds++; - if (this.loadedSeconds >= minTickUnit) { - this.loadedSeconds = 0; - this.tickedBlocks.clear(); - this.queue.clear(); - if (setting.isScheduledTick()) { - this.arrangeTasks(minTickUnit); - } - } - scheduledTick(setting, true); - randomTick(setting, true); - } - } - - public void setWorld(CWorld cWorld) { - this.cWorld = cWorld; - } - - @Override - public CustomCropsWorld getCustomCropsWorld() { - return cWorld; - } - - @Override - public CustomCropsRegion getCustomCropsRegion() { - return cWorld.getLoadedRegionAt(chunkPos.getRegionPos()).orElse(null); - } - - @Override - public ChunkPos getChunkPos() { - return chunkPos; - } - - @Override - public void secondTimer() { - WorldSetting setting = cWorld.getWorldSetting(); - int interval = setting.getMinTickUnit(); - this.loadedSeconds++; - // if loadedSeconds reach another recycle, rearrange the tasks - if (this.loadedSeconds >= interval) { - this.loadedSeconds = 0; - this.tickedBlocks.clear(); - this.queue.clear(); - if (setting.isScheduledTick()) { - this.arrangeTasks(interval); - } - } - - scheduledTick(setting, false); - randomTick(setting, false); - } - - private void scheduledTick(WorldSetting setting, boolean offline) { - // scheduled tick - while (!queue.isEmpty() && queue.peek().getTime() <= loadedSeconds) { - TickTask task = queue.poll(); - if (task != null) { - BlockPos pos = task.getChunkPos(); - CSection section = loadedSections.get(pos.getSectionID()); - if (section != null) { - CustomCropsBlock block = section.getBlockAt(pos); - if (block == null) continue; - switch (block.getType()) { - case SCARECROW, GREENHOUSE -> {} - case POT -> { - if (!setting.randomTickPot()) { - block.tick(setting.getTickPotInterval(), offline); - } - } - case CROP -> { - if (!setting.randomTickCrop()) { - block.tick(setting.getTickCropInterval(), offline); - } - } - case SPRINKLER -> { - if (!setting.randomTickSprinkler()) { - block.tick(setting.getTickSprinklerInterval(), offline); - } - } - } - } - } - } - } - - private void randomTick(WorldSetting setting, boolean offline) { - // random tick - ThreadLocalRandom random = ThreadLocalRandom.current(); - int randomTicks = setting.getRandomTickSpeed(); - for (CustomCropsSection section : getSections()) { - int sectionID = section.getSectionID(); - int baseY = sectionID * 16; - for (int i = 0; i < randomTicks; i++) { - int x = random.nextInt(16); - int y = random.nextInt(16) + baseY; - int z = random.nextInt(16); - CustomCropsBlock block = section.getBlockAt(new BlockPos(x,y,z)); - if (block != null) { - switch (block.getType()) { - case CROP -> { - if (setting.randomTickCrop()) { - block.tick(setting.getTickCropInterval(), offline); - } - } - case SPRINKLER -> { - if (setting.randomTickSprinkler()) { - block.tick(setting.getTickSprinklerInterval(), offline); - } - } - case POT -> { - ((WorldPot) block).tickWater(); - if (setting.randomTickPot()) { - block.tick(setting.getTickPotInterval(), offline); - } - } - } - } - } - } - } - - @Override - public long getLastLoadedTime() { - return lastLoadedTime; - } - - @Override - public int getLoadedSeconds() { - return this.loadedSeconds; - } - - public void arrangeTasks(int unit) { - ThreadLocalRandom random = ThreadLocalRandom.current(); - for (CustomCropsSection section : getSections()) { - for (Map.Entry entry : section.getBlockMap().entrySet()) { - this.queue.add(new TickTask( - random.nextInt(0, unit), - entry.getKey() - )); - this.tickedBlocks.add(entry.getKey()); - } - } - } - - public void tryCreatingTaskForNewBlock(BlockPos pos) { - WorldSetting setting = cWorld.getWorldSetting(); - if (setting.isScheduledTick() && !tickedBlocks.contains(pos)) { - tickedBlocks.add(pos); - int random = ThreadLocalRandom.current().nextInt(0, setting.getMinTickUnit()); - if (random > loadedSeconds) { - queue.add(new TickTask(random, pos)); - } - } - } - - @Override - public Optional getCropAt(SimpleLocation simpleLocation) { - return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldCrop worldCrop ? worldCrop : null); - } - - @Override - public Optional getSprinklerAt(SimpleLocation simpleLocation) { - return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldSprinkler worldSprinkler ? worldSprinkler : null); - } - - @Override - public Optional getPotAt(SimpleLocation simpleLocation) { - return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldPot worldPot ? worldPot : null); - } - - @Override - public Optional getGlassAt(SimpleLocation simpleLocation) { - return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldGlass worldGlass ? worldGlass : null); - } - - @Override - public Optional getScarecrowAt(SimpleLocation simpleLocation) { - return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldScarecrow worldScarecrow ? worldScarecrow : null); - } - - @Override - public void addWaterToSprinkler(Sprinkler sprinkler, int amount, SimpleLocation location) { - Optional optionalSprinkler = getSprinklerAt(location); - if (optionalSprinkler.isEmpty()) { - addBlockAt(new MemorySprinkler(location, sprinkler.getKey(), amount), location); - CustomCropsPlugin.get().debug("When adding water to sprinkler at " + location + ", the sprinkler data doesn't exist."); - if (sprinkler.get3DItemWithWater() != null) { - CustomCropsPlugin.get().getItemManager().removeAnythingAt(location.getBukkitLocation()); - CustomCropsPlugin.get().getItemManager().placeItem(location.getBukkitLocation(), sprinkler.getItemCarrier(), sprinkler.get3DItemWithWater()); - } - } else { - int current = optionalSprinkler.get().getWater(); - if (current == 0) { - if (sprinkler.get3DItemWithWater() != null) { - CustomCropsPlugin.get().getItemManager().removeAnythingAt(location.getBukkitLocation()); - CustomCropsPlugin.get().getItemManager().placeItem(location.getBukkitLocation(), sprinkler.getItemCarrier(), sprinkler.get3DItemWithWater()); - } - } - optionalSprinkler.get().setWater(current + amount); - } - } - - @Override - public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location) { - Optional optionalWorldPot = getPotAt(location); - if (optionalWorldPot.isEmpty()) { - MemoryPot memoryPot = new MemoryPot(location, pot.getKey()); - memoryPot.setFertilizer(fertilizer); - addBlockAt(memoryPot, location); - CustomCropsPlugin.get().debug("When adding fertilizer to pot at " + location + ", the pot data doesn't exist."); - CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, false, fertilizer); - } else { - optionalWorldPot.get().setFertilizer(fertilizer); - CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, optionalWorldPot.get().getWater() > 0, fertilizer); - } - } - - @Override - public void addWaterToPot(Pot pot, int amount, SimpleLocation location) { - Optional optionalWorldPot = getPotAt(location); - if (optionalWorldPot.isEmpty()) { - MemoryPot memoryPot = new MemoryPot(location, pot.getKey()); - memoryPot.setWater(amount); - addBlockAt(memoryPot, location); - CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, true, null); - CustomCropsPlugin.get().debug("When adding water to pot at " + location + ", the pot data doesn't exist."); - } else { - optionalWorldPot.get().setWater(optionalWorldPot.get().getWater() + amount); - CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, true, optionalWorldPot.get().getFertilizer()); - } - } - - @Override - public void addPotAt(WorldPot pot, SimpleLocation location) { - CustomCropsBlock previous = addBlockAt(pot, location); - if (previous != null) { - if (previous instanceof WorldPot) { - CustomCropsPlugin.get().debug("Found duplicated pot data when adding pot at " + location); - } else { - CustomCropsPlugin.get().debug("Found unremoved data when adding crop at " + location + ". Previous type is " + previous.getType().name()); - } - } - } - - @Override - public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) { - CustomCropsBlock previous = addBlockAt(sprinkler, location); - if (previous != null) { - if (previous instanceof WorldSprinkler) { - CustomCropsPlugin.get().debug("Found duplicated sprinkler data when adding sprinkler at " + location); - } else { - CustomCropsPlugin.get().debug("Found unremoved data when adding crop at " + location + ". Previous type is " + previous.getType().name()); - } - } - } - - @Override - public void addCropAt(WorldCrop crop, SimpleLocation location) { - CustomCropsBlock previous = addBlockAt(crop, location); - if (previous != null) { - if (previous instanceof WorldCrop) { - CustomCropsPlugin.get().debug("Found duplicated crop data when adding crop at " + location); - } else { - CustomCropsPlugin.get().debug("Found unremoved data when adding crop at " + location + ". Previous type is " + previous.getType().name()); - } - } - } - - @Override - public void addPointToCrop(Crop crop, int points, SimpleLocation location) { - if (points <= 0) return; - Optional cropData = getCropAt(location); - if (cropData.isEmpty()) { - return; - } - WorldCrop worldCrop = cropData.get(); - int previousPoint = worldCrop.getPoint(); - int x = Math.min(previousPoint + points, crop.getMaxPoints()); - worldCrop.setPoint(x); - Location bkLoc = location.getBukkitLocation(); - if (bkLoc == null) return; - for (int i = previousPoint + 1; i <= x; i++) { - Crop.Stage stage = crop.getStageByPoint(i); - if (stage != null) { - stage.trigger(ActionTrigger.GROW, new State(null, new ItemStack(Material.AIR), bkLoc)); - } - } - String pre = crop.getStageItemByPoint(previousPoint); - String after = crop.getStageItemByPoint(x); - if (pre.equals(after)) return; - CRotation CRotation = CustomCropsPlugin.get().getItemManager().removeAnythingAt(bkLoc); - CustomCropsPlugin.get().getItemManager().placeItem(bkLoc, crop.getItemCarrier(), after, CRotation); - } - - @Override - public void addGlassAt(WorldGlass glass, SimpleLocation location) { - CustomCropsBlock previous = addBlockAt(glass, location); - if (previous != null) { - if (previous instanceof WorldGlass) { - CustomCropsPlugin.get().debug("Found duplicated glass data when adding crop at " + location); - } else { - CustomCropsPlugin.get().debug("Found unremoved data when adding glass at " + location + ". Previous type is " + previous.getType().name()); - } - } - } - - @Override - public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { - CustomCropsBlock previous = addBlockAt(scarecrow, location); - if (previous != null) { - if (previous instanceof WorldScarecrow) { - CustomCropsPlugin.get().debug("Found duplicated scarecrow data when adding scarecrow at " + location); - } else { - CustomCropsPlugin.get().debug("Found unremoved data when adding scarecrow at " + location + ". Previous type is " + previous.getType().name()); - } - } - } - - @Override - public WorldSprinkler removeSprinklerAt(SimpleLocation location) { - CustomCropsBlock removed = removeBlockAt(location); - if (removed == null) { - CustomCropsPlugin.get().debug("Failed to remove sprinkler from " + location + " because sprinkler doesn't exist."); - return null; - } else if (!(removed instanceof WorldSprinkler worldSprinkler)) { - CustomCropsPlugin.get().debug("Removed sprinkler from " + location + " but the previous block type is " + removed.getType().name()); - return null; - } else { - return worldSprinkler; - } - } - - @Override - public WorldPot removePotAt(SimpleLocation location) { - CustomCropsBlock removed = removeBlockAt(location); - if (removed == null) { - CustomCropsPlugin.get().debug("Failed to remove pot from " + location + " because pot doesn't exist."); - return null; - } else if (!(removed instanceof WorldPot worldPot)) { - CustomCropsPlugin.get().debug("Removed pot from " + location + " but the previous block type is " + removed.getType().name()); - return null; - } else { - return worldPot; - } - } - - @Override - public WorldCrop removeCropAt(SimpleLocation location) { - CustomCropsBlock removed = removeBlockAt(location); - if (removed == null) { - CustomCropsPlugin.get().debug("Failed to remove crop from " + location + " because crop doesn't exist."); - return null; - } else if (!(removed instanceof WorldCrop worldCrop)) { - CustomCropsPlugin.get().debug("Removed crop from " + location + " but the previous block type is " + removed.getType().name()); - return null; - } else { - return worldCrop; - } - } - - @Override - public WorldGlass removeGlassAt(SimpleLocation location) { - CustomCropsBlock removed = removeBlockAt(location); - if (removed == null) { - CustomCropsPlugin.get().debug("Failed to remove glass from " + location + " because glass doesn't exist."); - return null; - } else if (!(removed instanceof WorldGlass worldGlass)) { - CustomCropsPlugin.get().debug("Removed glass from " + location + " but the previous block type is " + removed.getType().name()); - return null; - } else { - return worldGlass; - } - } - - @Override - public WorldScarecrow removeScarecrowAt(SimpleLocation location) { - CustomCropsBlock removed = removeBlockAt(location); - if (removed == null) { - CustomCropsPlugin.get().debug("Failed to remove scarecrow from " + location + " because scarecrow doesn't exist."); - return null; - } else if (!(removed instanceof WorldScarecrow worldScarecrow)) { - CustomCropsPlugin.get().debug("Removed scarecrow from " + location + " but the previous block type is " + removed.getType().name()); - return null; - } else { - return worldScarecrow; - } - } - - @Override - public CustomCropsBlock removeBlockAt(SimpleLocation location) { - BlockPos pos = BlockPos.getByLocation(location); - CSection section = loadedSections.get(pos.getSectionID()); - if (section == null) return null; - return section.removeBlockAt(pos); - } - - @Override - public CustomCropsBlock addBlockAt(CustomCropsBlock block, SimpleLocation location) { - BlockPos pos = BlockPos.getByLocation(location); - CSection section = loadedSections.get(pos.getSectionID()); - if (section == null) { - section = new CSection(pos.getSectionID()); - loadedSections.put(pos.getSectionID(), section); - } - this.tryCreatingTaskForNewBlock(pos); - return section.addBlockAt(pos, block); - } - - @Override - public Optional getBlockAt(SimpleLocation location) { - BlockPos pos = BlockPos.getByLocation(location); - CSection section = loadedSections.get(pos.getSectionID()); - if (section == null) { - return Optional.empty(); - } - return Optional.ofNullable(section.getBlockAt(pos)); - } - - @Override - public int getCropAmount() { - int amount = 0; - for (CustomCropsSection section : getSections()) { - for (CustomCropsBlock block : section.getBlocks()) { - if (block instanceof WorldCrop) { - amount++; - } - } - } - return amount; - } - - @Override - public int getPotAmount() { - int amount = 0; - for (CustomCropsSection section : getSections()) { - for (CustomCropsBlock block : section.getBlocks()) { - if (block instanceof WorldPot) { - amount++; - } - } - } - return amount; - } - - @Override - public int getSprinklerAmount() { - int amount = 0; - for (CustomCropsSection section : getSections()) { - for (CustomCropsBlock block : section.getBlocks()) { - if (block instanceof WorldSprinkler) { - amount++; - } - } - } - return amount; - } - - @Override - public boolean hasScarecrow() { - for (CustomCropsSection section : getSections()) { - for (CustomCropsBlock block : section.getBlocks()) { - if (block instanceof WorldScarecrow) { - return true; - } - } - } - return false; - } - - public CSection[] getSectionsForSerialization() { - ArrayList sections = new ArrayList<>(); - for (Map.Entry entry : loadedSections.entrySet()) { - if (!entry.getValue().canPrune()) { - sections.add(entry.getValue()); - } - } - return sections.toArray(new CSection[0]); - } - - @Override - public CustomCropsSection[] getSections() { - return loadedSections.values().toArray(new CustomCropsSection[0]); - } - - @Override - public CustomCropsSection getSection(int sectionID) { - return loadedSections.get(sectionID); - } - - @Override - public int getUnloadedSeconds() { - return unloadedSeconds; - } - - @Override - public void setUnloadedSeconds(int unloadedSeconds) { - this.unloadedSeconds = unloadedSeconds; - } - - @Override - public void resetUnloadedSeconds() { - this.unloadedSeconds = 0; - this.notified = false; - } - - @Override - public boolean canPrune() { - return loadedSections.isEmpty(); - } - - public PriorityQueue getQueue() { - return queue; - } - - public Set getTickedBlocks() { - return tickedBlocks; - } - - @Override - public boolean isOfflineTaskNotified() { - return notified; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CRegion.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CRegion.java deleted file mode 100644 index 5a6808e1a..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CRegion.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.RegionPos; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsRegion; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class CRegion implements CustomCropsRegion { - - private final CWorld cWorld; - private final RegionPos regionPos; - private final ConcurrentHashMap cachedChunkBytes; - - public CRegion(CWorld cWorld, RegionPos regionPos) { - this.cWorld = cWorld; - this.regionPos = regionPos; - this.cachedChunkBytes = new ConcurrentHashMap<>(); - } - - public CRegion(CWorld cWorld, RegionPos regionPos, ConcurrentHashMap cachedChunkBytes) { - this.cWorld = cWorld; - this.regionPos = regionPos; - this.cachedChunkBytes = cachedChunkBytes; - } - - @Override - public CustomCropsWorld getCustomCropsWorld() { - return cWorld; - } - - @Nullable - @Override - public byte[] getChunkBytes(ChunkPos pos) { - return cachedChunkBytes.get(pos); - } - - @Override - public RegionPos getRegionPos() { - return regionPos; - } - - @Override - public void removeChunk(ChunkPos pos) { - cachedChunkBytes.remove(pos); - } - - @Override - public void saveChunk(ChunkPos pos, byte[] data) { - cachedChunkBytes.put(pos, data); - } - - @Override - public Map getRegionDataToSave() { - return new HashMap<>(cachedChunkBytes); - } - - @Override - public boolean canPrune() { - return cachedChunkBytes.size() == 0; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CSection.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CSection.java deleted file mode 100644 index 454ed953f..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CSection.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import net.momirealms.customcrops.api.mechanic.world.BlockPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsSection; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class CSection implements CustomCropsSection { - - private final int sectionID; - private final ConcurrentHashMap blocks; - - public CSection(int sectionID) { - this.sectionID = sectionID; - this.blocks = new ConcurrentHashMap<>(); - } - - public CSection(int sectionID, ConcurrentHashMap blocks) { - this.blocks = blocks; - this.sectionID = sectionID; - } - - @Override - public int getSectionID() { - return sectionID; - } - - @Override - public CustomCropsBlock getBlockAt(BlockPos pos) { - return blocks.get(pos); - } - - @Override - public CustomCropsBlock removeBlockAt(BlockPos pos) { - return blocks.remove(pos); - } - - @Override - public CustomCropsBlock addBlockAt(BlockPos pos, CustomCropsBlock block) { - return blocks.put(pos, block); - } - - @Override - public boolean canPrune() { - return blocks.size() == 0; - } - - @Override - public CustomCropsBlock[] getBlocks() { - return blocks.values().toArray(new CustomCropsBlock[0]); - } - - @Override - public Map getBlockMap() { - return blocks; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java deleted file mode 100644 index 04dbc52fd..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.event.SeasonChangeEvent; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.VersionManager; -import net.momirealms.customcrops.api.manager.WorldManager; -import net.momirealms.customcrops.api.mechanic.item.Crop; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.item.Pot; -import net.momirealms.customcrops.api.mechanic.item.Sprinkler; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.RegionPos; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.*; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import net.momirealms.customcrops.api.scheduler.CancellableTask; -import net.momirealms.customcrops.api.scheduler.Scheduler; -import net.momirealms.customcrops.api.util.EventUtils; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class CWorld implements CustomCropsWorld { - - private final WorldManager worldManager; - private WeakReference world; - private final ConcurrentHashMap loadedChunks; - private final ConcurrentHashMap lazyChunks; - private final ConcurrentHashMap loadedRegions; - private WorldSetting setting; - private WorldInfoData infoData; - private final String worldName; - private CancellableTask worldTask; - private int currentMinecraftDay; - private int regionTimer; - - public CWorld(WorldManager worldManager, World world) { - this.world = new WeakReference<>(world); - this.worldManager = worldManager; - this.loadedChunks = new ConcurrentHashMap<>(); - this.lazyChunks = new ConcurrentHashMap<>(); - this.loadedRegions = new ConcurrentHashMap<>(); - this.worldName = world.getName(); - this.currentMinecraftDay = (int) (world.getFullTime() / 24000); - this.regionTimer = 0; - } - - @Override - public void save() { - long time1 = System.currentTimeMillis(); - worldManager.saveInfoData(this); - for (CChunk chunk : loadedChunks.values()) { - worldManager.saveChunkToCachedRegion(chunk); - } - for (CChunk chunk : lazyChunks.values()) { - worldManager.saveChunkToCachedRegion(chunk); - } - for (CRegion region : loadedRegions.values()) { - worldManager.saveRegionToFile(region); - } - long time2 = System.currentTimeMillis(); - CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to save world " + worldName + ". Saved " + (lazyChunks.size() + loadedChunks.size()) + " chunks."); - } - - @Override - public void startTick() { - if (this.worldTask == null || this.worldTask.isCancelled()) - this.worldTask = CustomCropsPlugin.get().getScheduler().runTaskAsyncTimer(this::timer, 1, 1, TimeUnit.SECONDS); - } - - @Override - public void cancelTick() { - if (!this.worldTask.isCancelled()) - this.worldTask.cancel(); - } - - private void timer() { - ArrayList> chunksToSave = new ArrayList<>(); - for (Map.Entry lazyEntry : lazyChunks.entrySet()) { - CChunk chunk = lazyEntry.getValue(); - int sec = chunk.getUnloadedSeconds() + 1; - if (sec >= 30) { - chunksToSave.add(Pair.of(lazyEntry.getKey(), chunk)); - } else { - chunk.setUnloadedSeconds(sec); - } - } - for (Pair pair : chunksToSave) { - lazyChunks.remove(pair.left()); - worldManager.saveChunkToCachedRegion(pair.right()); - } - if (setting.isAutoSeasonChange()) { - this.updateSeasonAndDate(); - } - if (setting.isSchedulerEnabled()) { - Scheduler scheduler = CustomCropsPlugin.get().getScheduler(); - if (VersionManager.folia()) { - for (CChunk chunk : loadedChunks.values()) { - if (unloadIfNotLoaded(chunk.getChunkPos())) { - continue; - } - scheduler.runTaskSync(chunk::secondTimer, getWorld(), chunk.getChunkPos().x(), chunk.getChunkPos().z()); - } - } else { - for (CChunk chunk : loadedChunks.values()) { - if (unloadIfNotLoaded(chunk.getChunkPos())) { - continue; - } - chunk.secondTimer(); - } - } - } - - // timer check to unload regions - this.regionTimer++; - if (this.regionTimer >= 600) { - this.regionTimer = 0; - ArrayList removed = new ArrayList<>(); - for (Map.Entry entry : loadedRegions.entrySet()) { - if (isRegionNoLongerLoaded(entry.getKey())) { - worldManager.saveRegionToFile(entry.getValue()); - removed.add(entry.getKey()); - } - } - for (RegionPos pos : removed) { - loadedRegions.remove(pos); - } - } - } - - private void updateSeasonAndDate() { - World bukkitWorld = getWorld(); - if (bukkitWorld == null) { - LogUtils.severe(String.format("World %s unloaded unexpectedly. Stop ticking task...", worldName)); - this.cancelTick(); - return; - } - - long ticks = bukkitWorld.getFullTime(); - int days = (int) (ticks / 24000); - if (days == this.currentMinecraftDay) { - return; - } - - if (days > this.currentMinecraftDay) { - int date = infoData.getDate(); - date++; - if (date > setting.getSeasonDuration()) { - date = 1; - Season next = infoData.getSeason().getNextSeason(); - infoData.setSeason(next); - EventUtils.fireAndForget(new SeasonChangeEvent(bukkitWorld, next)); - } - infoData.setDate(date); - } - this.currentMinecraftDay = days; - } - - @Override - public CustomCropsChunk removeLazyChunkAt(ChunkPos chunkPos) { - return lazyChunks.remove(chunkPos); - } - - @Override - public WorldSetting getWorldSetting() { - return setting; - } - - @Override - public void setWorldSetting(WorldSetting setting) { - this.setting = setting; - } - - @Override - public Collection getChunkStorage() { - return loadedChunks.values(); - } - - @Nullable - @Override - public World getWorld() { - return Optional.ofNullable(world.get()).orElseGet(() -> { - World bukkitWorld = Bukkit.getWorld(worldName); - if (bukkitWorld != null) { - this.world = new WeakReference<>(bukkitWorld); - } - return bukkitWorld; - }); - } - - @NotNull - @Override - public String getWorldName() { - return worldName; - } - - @Override - public boolean isChunkLoaded(ChunkPos chunkPos) { - return loadedChunks.containsKey(chunkPos); - } - - @Override - public boolean isRegionLoaded(RegionPos regionPos) { - return loadedRegions.containsKey(regionPos); - } - - @Override - public Optional getOrCreateLoadedChunkAt(ChunkPos chunkPos) { - return Optional.ofNullable(createOrGetChunk(chunkPos)); - } - - @Override - public Optional getLoadedChunkAt(ChunkPos chunkPos) { - return Optional.ofNullable(loadedChunks.get(chunkPos)); - } - - @Override - public Optional getLoadedRegionAt(RegionPos regionPos) { - return Optional.ofNullable(loadedRegions.get(regionPos)); - } - - @Override - public void loadRegion(CustomCropsRegion region) { - RegionPos regionPos = region.getRegionPos(); - if (loadedRegions.containsKey(regionPos)) { - LogUtils.warn("Invalid operation: Loaded region is loaded again." + regionPos); - return; - } - loadedRegions.put(regionPos, (CRegion) region); - } - - @Override - public void loadChunk(CustomCropsChunk chunk) { - ChunkPos chunkPos = chunk.getChunkPos(); - if (loadedChunks.containsKey(chunkPos)) { - LogUtils.warn("Invalid operation: Loaded chunk is loaded again." + chunkPos); - return; - } - loadedChunks.put(chunkPos, (CChunk) chunk); - } - - @Override - public void unloadChunk(ChunkPos chunkPos) { - CChunk chunk = loadedChunks.remove(chunkPos); - if (chunk != null) { - chunk.updateLastLoadedTime(); - lazyChunks.put(chunkPos, chunk); - } - } - - @Override - public void deleteChunk(ChunkPos chunkPos) { - CChunk chunk = loadedChunks.remove(chunkPos); - } - - @Override - public void setInfoData(WorldInfoData infoData) { - this.infoData = infoData; - } - - @Override - public WorldInfoData getInfoData() { - return infoData; - } - - @Override - public int getDate() { - if (setting.isEnableSeason()) { - if (ConfigManager.syncSeasons() && ConfigManager.referenceWorld() != world) { - return worldManager.getCustomCropsWorld(ConfigManager.referenceWorld()).map(customCropsWorld -> customCropsWorld.getInfoData().getDate()).orElse(0); - } - return infoData.getDate(); - } else { - return 0; - } - } - - @Override - @Nullable - public Season getSeason() { - if (setting.isEnableSeason()) { - if (ConfigManager.syncSeasons() && ConfigManager.referenceWorld() != world) { - return worldManager.getCustomCropsWorld(ConfigManager.referenceWorld()).map(customCropsWorld -> customCropsWorld.getInfoData().getSeason()).orElse(null); - } - return infoData.getSeason(); - } else { - return null; - } - } - - @Override - public Optional getSprinklerAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkPos()); - if (chunk == null) return Optional.empty(); - return chunk.getSprinklerAt(location); - } - - @Override - public Optional getPotAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkPos()); - if (chunk == null) return Optional.empty(); - return chunk.getPotAt(location); - } - - @Override - public Optional getCropAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkPos()); - if (chunk == null) return Optional.empty(); - return chunk.getCropAt(location); - } - - @Override - public Optional getGlassAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkPos()); - if (chunk == null) return Optional.empty(); - return chunk.getGlassAt(location); - } - - @Override - public Optional getScarecrowAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkPos()); - if (chunk == null) return Optional.empty(); - return chunk.getScarecrowAt(location); - } - - @Override - public Optional getBlockAt(SimpleLocation location) { - CChunk chunk = loadedChunks.get(location.getChunkPos()); - if (chunk == null) return Optional.empty(); - return chunk.getBlockAt(location); - } - - @Override - public void addWaterToSprinkler(Sprinkler sprinkler, int amount, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addWaterToSprinkler(sprinkler, amount, location); - } else { - LogUtils.warn("Invalid operation: Adding water to sprinkler in a not generated chunk"); - } - } - - @Override - public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addFertilizerToPot(pot, fertilizer, location); - } else { - LogUtils.warn("Invalid operation: Adding fertilizer to pot in a not generated chunk"); - } - } - - @Override - public void addWaterToPot(Pot pot, int amount, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addWaterToPot(pot, amount, location); - } else { - LogUtils.warn("Invalid operation: Adding water to pot in a not generated chunk"); - } - } - - @Override - public void addPotAt(WorldPot pot, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addPotAt(pot, location); - } else { - LogUtils.warn("Invalid operation: Adding pot in a not generated chunk"); - } - } - - @Override - public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addSprinklerAt(sprinkler, location); - } else { - LogUtils.warn("Invalid operation: Adding sprinkler in a not generated chunk"); - } - } - - @Override - public void addCropAt(WorldCrop crop, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addCropAt(crop, location); - } else { - LogUtils.warn("Invalid operation: Adding crop in a not generated chunk"); - } - } - - @Override - public void addPointToCrop(Crop crop, int points, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addPointToCrop(crop, points, location); - } else { - LogUtils.warn("Invalid operation: Adding point to crop in a not generated chunk"); - } - } - - @Override - public void addGlassAt(WorldGlass glass, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addGlassAt(glass, location); - } else { - LogUtils.warn("Invalid operation: Adding glass in a not generated chunk"); - } - } - - @Override - public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { - Optional chunk = getOrCreateLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - chunk.get().addScarecrowAt(scarecrow, location); - } else { - LogUtils.warn("Invalid operation: Adding scarecrow in a not generated chunk"); - } - } - - @Override - public WorldSprinkler removeSprinklerAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - return chunk.get().removeSprinklerAt(location); - } else { - LogUtils.warn("Invalid operation: Removing sprinkler from an unloaded/empty chunk"); - return null; - } - } - - @Override - public WorldPot removePotAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - return chunk.get().removePotAt(location); - } else { - LogUtils.warn("Invalid operation: Removing pot from an unloaded/empty chunk"); - return null; - } - } - - @Override - public WorldCrop removeCropAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - return chunk.get().removeCropAt(location); - } else { - LogUtils.warn("Invalid operation: Removing crop from an unloaded/empty chunk"); - return null; - } - } - - @Override - public WorldGlass removeGlassAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - return chunk.get().removeGlassAt(location); - } else { - LogUtils.warn("Invalid operation: Removing glass from an unloaded/empty chunk"); - return null; - } - } - - @Override - public WorldScarecrow removeScarecrowAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - return chunk.get().removeScarecrowAt(location); - } else { - LogUtils.warn("Invalid operation: Removing scarecrow from an unloaded/empty chunk"); - return null; - } - } - - @Override - public CustomCropsBlock removeAnythingAt(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isPresent()) { - return chunk.get().removeBlockAt(location); - } else { - LogUtils.warn("Invalid operation: Removing anything from an unloaded/empty chunk"); - return null; - } - } - - @Nullable - private CustomCropsChunk createOrGetChunk(ChunkPos chunkPos) { - World bukkitWorld = world.get(); - if (bukkitWorld == null) - return null; - CChunk chunk = loadedChunks.get(chunkPos); - if (chunk != null) { - return chunk; - } - // is a loaded chunk, but it doesn't have CustomCrops data - if (bukkitWorld.isChunkLoaded(chunkPos.x(), chunkPos.z())) { - chunk = new CChunk(this, chunkPos); - loadChunk(chunk); - return chunk; - } else { - return null; - } - } - - @Override - public boolean doesChunkHaveScarecrow(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - return chunk.map(CustomCropsChunk::hasScarecrow).orElse(false); - } - - @Override - public boolean isPotReachLimit(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isEmpty()) return false; - if (setting.getPotPerChunk() < 0) return false; - return chunk.get().getPotAmount() >= setting.getPotPerChunk(); - } - - @Override - public boolean isCropReachLimit(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isEmpty()) return false; - if (setting.getCropPerChunk() < 0) return false; - return chunk.get().getCropAmount() >= setting.getCropPerChunk(); - } - - @Override - public boolean isSprinklerReachLimit(SimpleLocation location) { - Optional chunk = getLoadedChunkAt(location.getChunkPos()); - if (chunk.isEmpty()) return false; - if (setting.getSprinklerPerChunk() < 0) return false; - return chunk.get().getSprinklerAmount() >= setting.getSprinklerPerChunk(); - } - - private boolean isRegionNoLongerLoaded(RegionPos region) { - World w = world.get(); - if (w != null) { - for (int chunkX = region.x() * 32; chunkX < region.x() * 32 + 32; chunkX++) { - for (int chunkZ = region.z() * 32; chunkZ < region.z() * 32 + 32; chunkZ++) { - // if a chunk is unloaded, then it should not be in the loaded chunks map - if (w.isChunkLoaded(chunkX, chunkZ) || lazyChunks.containsKey(ChunkPos.of(chunkX, chunkZ))) { - return false; - } - } - } - } - return true; - } - - private boolean unloadIfNotLoaded(ChunkPos pos) { - if (!world.get().isChunkLoaded(pos.x(), pos.z())) { - unloadChunk(pos); - return true; - } - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableChunk.java deleted file mode 100644 index b18e22387..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableChunk.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import java.util.List; - -public class SerializableChunk { - - private final int x; - private final int z; - private final int loadedSeconds; - private final long lastLoadedTime; - private final List sections; - private final int[] queuedTasks; - private final int[] ticked; - - public SerializableChunk(int x, int z, int loadedSeconds, long lastLoadedTime, List sections, int[] queuedTasks, int[] ticked) { - this.x = x; - this.z = z; - this.lastLoadedTime = lastLoadedTime; - this.loadedSeconds = loadedSeconds; - this.sections = sections; - this.queuedTasks = queuedTasks; - this.ticked = ticked; - } - - public int getLoadedSeconds() { - return loadedSeconds; - } - - public int getX() { - return x; - } - - public int getZ() { - return z; - } - - public long getLastLoadedTime() { - return lastLoadedTime; - } - - public List getSections() { - return sections; - } - - public int[] getQueuedTasks() { - return queuedTasks; - } - - public int[] getTicked() { - return ticked; - } - - public boolean canPrune() { - return sections.size() == 0; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableSection.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableSection.java deleted file mode 100644 index 9184cd9d5..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableSection.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import com.flowpowered.nbt.CompoundTag; - -import java.util.List; - -public class SerializableSection { - - private final int sectionID; - private final List blocks; - - public SerializableSection(int sectionID, List blocks) { - this.sectionID = sectionID; - this.blocks = blocks; - } - - public int getSectionID() { - return sectionID; - } - - public List getBlocks() { - return blocks; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java deleted file mode 100644 index fa1d9d82b..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.WorldManager; -import net.momirealms.customcrops.api.mechanic.item.*; -import net.momirealms.customcrops.api.mechanic.misc.MatchRule; -import net.momirealms.customcrops.api.mechanic.world.AbstractWorldAdaptor; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.*; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.world.adaptor.BukkitWorldAdaptor; -import net.momirealms.customcrops.mechanic.world.adaptor.SlimeWorldAdaptor; -import net.momirealms.customcrops.mechanic.world.block.*; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkLoadEvent; -import org.bukkit.event.world.ChunkUnloadEvent; -import org.bukkit.event.world.EntitiesLoadEvent; -import org.jetbrains.annotations.NotNull; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -public class WorldManagerImpl implements WorldManager, Listener { - - private final CustomCropsPlugin plugin; - private final ConcurrentHashMap loadedWorlds; - private final HashMap worldSettingMap; - private WorldSetting defaultWorldSetting; - private MatchRule matchRule; - private HashSet worldList; - private AbstractWorldAdaptor worldAdaptor; - private String absoluteWorldFolder; - - public WorldManagerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.loadedWorlds = new ConcurrentHashMap<>(); - this.worldSettingMap = new HashMap<>(); - try { - // to ignore the warning from asp - Class.forName("com.infernalsuite.aswm.api.SlimePlugin"); - this.worldAdaptor = new SlimeWorldAdaptor(this, 1); - } catch (ClassNotFoundException e) { - if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldPlugin")) { - this.worldAdaptor = new SlimeWorldAdaptor(this, 2); - } else { - this.worldAdaptor = new BukkitWorldAdaptor(this); - } - } - } - - @Override - public void load() { - this.registerListener(); - this.loadConfig(); - if (this.worldAdaptor instanceof BukkitWorldAdaptor adaptor) { - adaptor.setWorldFolder(absoluteWorldFolder); - } - for (World world : Bukkit.getWorlds()) { - if (isMechanicEnabled(world)) { - loadWorld(world); - } else { - unloadWorld(world); - } - } - } - - @Override - public void unload() { - this.unregisterListener(); - this.worldSettingMap.clear(); - } - - @Override - public void disable() { - this.unload(); - for (World world : Bukkit.getWorlds()) { - unloadWorld(world); - } - if (!this.loadedWorlds.isEmpty()) { - LogUtils.severe("Detected that some worlds are not properly unloaded. " + - "You can safely ignore this if you are editing \"worlds.list\" and restarting to apply it"); - for (String world : this.loadedWorlds.keySet()) { - LogUtils.severe(" - " + world); - } - for (CustomCropsWorld world : this.loadedWorlds.values()) { - worldAdaptor.unload(world); - } - this.loadedWorlds.clear(); - } - } - - private void loadConfig() { - YamlConfiguration config = ConfigUtils.getConfig("config.yml"); - - ConfigurationSection section = config.getConfigurationSection("worlds"); - if (section == null) { - LogUtils.severe("worlds section should not be null"); - return; - } - - this.absoluteWorldFolder = section.getString("absolute-world-folder-path",""); - this.matchRule = MatchRule.valueOf(section.getString("mode", "blacklist").toUpperCase(Locale.ENGLISH)); - this.worldList = new HashSet<>(section.getStringList("list")); - - // limitation - ConfigurationSection settingSection = section.getConfigurationSection("settings"); - if (settingSection == null) { - LogUtils.severe("worlds.settings section should not be null"); - return; - } - - ConfigurationSection defaultSection = settingSection.getConfigurationSection("_DEFAULT_"); - if (defaultSection == null) { - LogUtils.severe("worlds.settings._DEFAULT_ section should not be null"); - return; - } - - this.defaultWorldSetting = ConfigUtils.getWorldSettingFromSection(defaultSection); - ConfigurationSection worldSection = settingSection.getConfigurationSection("_WORLDS_"); - if (worldSection != null) { - for (Map.Entry entry : worldSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - this.worldSettingMap.put(entry.getKey(), ConfigUtils.getWorldSettingFromSection(inner)); - } - } - } - } - - private void registerListener() { - Bukkit.getPluginManager().registerEvents(this, plugin); - if (worldAdaptor != null) - Bukkit.getPluginManager().registerEvents(worldAdaptor, plugin); - } - - private void unregisterListener() { - HandlerList.unregisterAll(this); - if (worldAdaptor != null) - HandlerList.unregisterAll(worldAdaptor); - } - - @NotNull - @Override - public CustomCropsWorld loadWorld(@NotNull World world) { - String worldName = world.getName(); - if (loadedWorlds.containsKey(worldName)) { - CWorld cWorld = loadedWorlds.get(worldName); - cWorld.setWorldSetting(getInitWorldSetting(world)); - return cWorld; - } - CWorld cWorld = new CWorld(this, world); - worldAdaptor.init(cWorld); - loadedWorlds.put(worldName, cWorld); - cWorld.setWorldSetting(getInitWorldSetting(world)); - cWorld.startTick(); - for (Chunk chunk : world.getLoadedChunks()) { - handleChunkLoad(chunk); - } - return cWorld; - } - - @Override - public boolean unloadWorld(@NotNull World world) { - CustomCropsWorld customCropsWorld = loadedWorlds.remove(world.getName()); - if (customCropsWorld != null) { - customCropsWorld.cancelTick(); - worldAdaptor.unload(customCropsWorld); - return true; - } - return false; - } - - private WorldSetting getInitWorldSetting(World world) { - if (worldSettingMap.containsKey(world.getName())) - return worldSettingMap.get(world.getName()).clone(); - return defaultWorldSetting.clone(); - } - - @Override - public boolean isMechanicEnabled(@NotNull World world) { - if (matchRule == MatchRule.WHITELIST) { - return worldList.contains(world.getName()); - } else if (matchRule == MatchRule.BLACKLIST) { - return !worldList.contains(world.getName()); - } else { - for (String regex : worldList) { - if (world.getName().matches(regex)) { - return true; - } - } - return false; - } - } - - @NotNull - @Override - public Collection getWorldNames() { - return loadedWorlds.keySet(); - } - - @NotNull - @Override - public Collection getBukkitWorlds() { - return loadedWorlds.keySet().stream().map(Bukkit::getWorld).toList(); - } - - @NotNull - @Override - public Collection getCustomCropsWorlds() { - return loadedWorlds.values(); - } - - @NotNull - @Override - public Optional getCustomCropsWorld(@NotNull String name) { - return Optional.ofNullable(loadedWorlds.get(name)); - } - - @NotNull - @Override - public Optional getCustomCropsWorld(@NotNull World world) { - return Optional.ofNullable(loadedWorlds.get(world.getName())); - } - - @NotNull - @Override - public Optional getSprinklerAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) return Optional.empty(); - return cWorld.getSprinklerAt(location); - } - - @NotNull - @Override - public Optional getPotAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) return Optional.empty(); - return cWorld.getPotAt(location); - } - - @NotNull - @Override - public Optional getCropAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) return Optional.empty(); - return cWorld.getCropAt(location); - } - - @NotNull - @Override - public Optional getGlassAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) return Optional.empty(); - return cWorld.getGlassAt(location); - } - - @NotNull - @Override - public Optional getScarecrowAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) return Optional.empty(); - return cWorld.getScarecrowAt(location); - } - - @Override - public Optional getBlockAt(SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) return Optional.empty(); - return cWorld.getBlockAt(location); - } - - @Override - public WorldCrop createCropData(SimpleLocation location, Crop crop, int point) { - return new MemoryCrop(location, crop.getKey(), point); - } - - @Override - public WorldSprinkler createSprinklerData(SimpleLocation location, Sprinkler sprinkler, int water) { - return new MemorySprinkler(location, sprinkler.getKey(), water); - } - - @Override - public WorldPot createPotData(SimpleLocation location, Pot pot, int water, Fertilizer fertilizer, int fertilizerTimes) { - return new MemoryPot(location, pot.getKey(), water, fertilizer == null ? "" : fertilizer.getKey(), fertilizerTimes); - } - - @Override - public WorldGlass createGreenhouseGlassData(SimpleLocation location) { - return new MemoryGlass(location); - } - - @Override - public WorldScarecrow createScarecrowData(SimpleLocation location) { - return new MemoryScarecrow(location); - } - - @Override - public void addWaterToSprinkler(@NotNull Sprinkler sprinkler, @NotNull SimpleLocation location, int amount) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding water to sprinkler in unloaded world " + location); - return; - } - cWorld.addWaterToSprinkler(sprinkler, amount, location); - } - - @Override - public void addFertilizerToPot(@NotNull Pot pot, @NotNull Fertilizer fertilizer, @NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding fertilizer to pot in unloaded world " + location); - return; - } - cWorld.addFertilizerToPot(pot, fertilizer, location); - } - - @Override - public void addWaterToPot(@NotNull Pot pot, int amount, @NotNull SimpleLocation location) { - if (amount <= 0) return; - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding water to pot in unloaded world " + location); - return; - } - cWorld.addWaterToPot(pot, amount, location); - } - - @Override - public void addPotAt(@NotNull WorldPot pot, @NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding pot in unloaded world " + location); - return; - } - cWorld.addPotAt(pot, location); - } - - @Override - public void addSprinklerAt(@NotNull WorldSprinkler sprinkler, @NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding sprinkler in unloaded world " + location); - return; - } - cWorld.addSprinklerAt(sprinkler, location); - } - - @Override - public void addCropAt(@NotNull WorldCrop crop, @NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding crop in unloaded world " + location); - return; - } - cWorld.addCropAt(crop, location); - } - - @Override - public void addPointToCrop(@NotNull Crop crop, int points, @NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding point to crop in unloaded world " + location); - return; - } - cWorld.addPointToCrop(crop, points, location); - } - - @Override - public void addGlassAt(@NotNull WorldGlass glass, @NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding glass in unloaded world " + location); - return; - } - cWorld.addGlassAt(glass, location); - } - - @Override - public void addScarecrowAt(@NotNull WorldScarecrow scarecrow, @NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Adding scarecrow in unloaded world " + location); - return; - } - cWorld.addScarecrowAt(scarecrow, location); - } - - @Override - public WorldSprinkler removeSprinklerAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Removing sprinkler from unloaded world " + location); - return null; - } - return cWorld.removeSprinklerAt(location); - } - - @Override - public WorldPot removePotAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Removing pot from unloaded world " + location); - return null; - } - return cWorld.removePotAt(location); - } - - @Override - public WorldCrop removeCropAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Removing crop from unloaded world " + location); - return null; - } - return cWorld.removeCropAt(location); - } - - @Override - public WorldGlass removeGlassAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Removing glass from unloaded world " + location); - return null; - } - return cWorld.removeGlassAt(location); - } - - @Override - public WorldScarecrow removeScarecrowAt(@NotNull SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Removing scarecrow from unloaded world " + location); - return null; - } - return cWorld.removeScarecrowAt(location); - } - - @Override - public CustomCropsBlock removeAnythingAt(SimpleLocation location) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Removing anything from unloaded world " + location); - return null; - } - return cWorld.removeAnythingAt(location); - } - - @Override - public boolean isReachLimit(SimpleLocation location, ItemType itemType) { - CWorld cWorld = loadedWorlds.get(location.getWorldName()); - if (cWorld == null) { - LogUtils.warn("Unsupported operation: Querying amount in an unloaded world " + location); - return true; - } - switch (itemType) { - case CROP -> { - return cWorld.isCropReachLimit(location); - } - case SPRINKLER -> { - return cWorld.isSprinklerReachLimit(location); - } - case POT -> { - return cWorld.isPotReachLimit(location); - } - default -> { - return false; - } - } - } - - /** - * Still need further investigations into why chunk load event is called twice - */ - @Override - public void handleChunkLoad(Chunk bukkitChunk) { - Optional optional = getCustomCropsWorld(bukkitChunk.getWorld()); - if (optional.isEmpty()) - return; - - CustomCropsWorld customCropsWorld = optional.get(); - ChunkPos chunkPos = ChunkPos.getByBukkitChunk(bukkitChunk); - - if (customCropsWorld.isChunkLoaded(chunkPos)) { - return; - } - - // load chunks - this.worldAdaptor.loadChunkData(customCropsWorld, chunkPos); - - // offline grow part - if (!customCropsWorld.getWorldSetting().isOfflineTick()) return; - - // If chunk data not exists, return - Optional optionalChunk = customCropsWorld.getLoadedChunkAt(chunkPos); - if (optionalChunk.isEmpty()) { - return; - } - - CustomCropsChunk chunk = optionalChunk.get(); - - // load the entities if not loaded - bukkitChunk.getEntities(); - if (bukkitChunk.isEntitiesLoaded()) { - chunk.notifyOfflineUpdates(); - } - } - - @Override - public void handleChunkUnload(Chunk bukkitChunk) { - Optional optional = getCustomCropsWorld(bukkitChunk.getWorld()); - if (optional.isEmpty()) - return; - - CustomCropsWorld customCropsWorld = optional.get(); - ChunkPos chunkPos = ChunkPos.getByBukkitChunk(bukkitChunk); - - this.worldAdaptor.unloadChunkData(customCropsWorld, chunkPos); - } - - @EventHandler - public void onChunkLoad(ChunkLoadEvent event) { - handleChunkLoad(event.getChunk()); - } - - @EventHandler - public void onChunkUnLoad(ChunkUnloadEvent event) { - handleChunkUnload(event.getChunk()); - } - - @EventHandler - public void onEntitiesLoad(EntitiesLoadEvent event) { - - Chunk bukkitChunk = event.getChunk(); - Optional optional = getCustomCropsWorld(bukkitChunk.getWorld()); - if (optional.isEmpty()) - return; - - CustomCropsWorld customCropsWorld = optional.get(); - // offline grow part - if (!customCropsWorld.getWorldSetting().isOfflineTick()) return; - - ChunkPos chunkPos = ChunkPos.getByBukkitChunk(bukkitChunk); - - Optional optionalChunk = customCropsWorld.getLoadedChunkAt(chunkPos); - if (optionalChunk.isEmpty()) { - return; - } - - if (!optionalChunk.get().isOfflineTaskNotified()) { - optionalChunk.get().notifyOfflineUpdates(); - } - } - - @Override - public void saveChunkToCachedRegion(CustomCropsChunk chunk) { - this.worldAdaptor.saveChunkToCachedRegion(chunk); - } - - @Override - public void saveRegionToFile(CustomCropsRegion region) { - this.worldAdaptor.saveRegion(region); - } - - @Override - public AbstractWorldAdaptor getWorldAdaptor() { - return worldAdaptor; - } - - @Override - public void saveInfoData(CustomCropsWorld customCropsWorld) { - this.worldAdaptor.saveInfoData(customCropsWorld); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java deleted file mode 100644 index be3794506..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java +++ /dev/null @@ -1,729 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.adaptor; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.IntArrayTag; -import com.flowpowered.nbt.StringTag; -import com.flowpowered.nbt.stream.NBTInputStream; -import com.flowpowered.nbt.stream.NBTOutputStream; -import com.github.luben.zstd.Zstd; -import com.google.gson.Gson; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.manager.VersionManager; -import net.momirealms.customcrops.api.manager.WorldManager; -import net.momirealms.customcrops.api.mechanic.world.*; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsRegion; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; -import net.momirealms.customcrops.api.mechanic.world.level.WorldInfoData; -import net.momirealms.customcrops.api.mechanic.world.season.Season; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.sprinkler.Sprinkler; -import net.momirealms.customcrops.api.object.world.CCChunk; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.world.*; -import net.momirealms.customcrops.mechanic.world.block.*; -import net.momirealms.customcrops.scheduler.task.TickTask; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.world.WorldLoadEvent; -import org.bukkit.event.world.WorldSaveEvent; -import org.bukkit.event.world.WorldUnloadEvent; -import org.bukkit.persistence.PersistentDataType; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -public class BukkitWorldAdaptor extends AbstractWorldAdaptor { - - private static final NamespacedKey key = new NamespacedKey(CustomCropsPlugin.get(), "data"); - protected final Gson gson; - private String worldFolder; - - public BukkitWorldAdaptor(WorldManager worldManager) { - super(worldManager); - this.gson = new Gson(); - this.worldFolder = ""; - } - - @Override - public void unload(CustomCropsWorld customCropsWorld) { - World world = customCropsWorld.getWorld(); - if (world != null) { - getWorldFolder(world).mkdir(); - customCropsWorld.save(); - } - } - - @Override - public void saveInfoData(CustomCropsWorld customCropsWorld) { - CWorld cWorld = (CWorld) customCropsWorld; - World world = cWorld.getWorld(); - if (world == null) { - LogUtils.severe("Unexpected issue: World " + cWorld.getWorldName() + " unloaded before data saved"); - return; - } - - if (VersionManager.isHigherThan1_18()) { - world.getPersistentDataContainer().set(key, PersistentDataType.STRING, - gson.toJson(cWorld.getInfoData())); - } else { - try (FileWriter file = new FileWriter(new File(getWorldFolder(world), "cworld.dat"))) { - gson.toJson(cWorld.getInfoData(), file); - } catch (IOException ioException) { - ioException.printStackTrace(); - } - } - } - - @Override - public void init(CustomCropsWorld customCropsWorld) { - CWorld cWorld = (CWorld) customCropsWorld; - World world = cWorld.getWorld(); - if (world == null) { - LogUtils.severe("Unexpected issue: World " + cWorld.getWorldName() + " unloaded before data loaded"); - return; - } - - // create directory - new File(world.getWorldFolder(), "customcrops").mkdir(); - - // try converting legacy worlds - if (ConfigManager.convertWorldOnLoad()) { - if (convertWorldFromV33toV34(cWorld, world)) { - return; - } - } - - if (VersionManager.isHigherThan1_18()) { - // init world basic info - String json = world.getPersistentDataContainer().get(key, PersistentDataType.STRING); - WorldInfoData data = (json == null || json.equals("null")) ? WorldInfoData.empty() : gson.fromJson(json, WorldInfoData.class); - if (data == null) data = WorldInfoData.empty(); - cWorld.setInfoData(data); - } else { - File cWorldFile = new File(getWorldFolder(world), "cworld.dat"); - if (cWorldFile.exists()) { - byte[] fileBytes = new byte[(int) cWorldFile.length()]; - try (FileInputStream fis = new FileInputStream(cWorldFile)) { - fis.read(fileBytes); - } catch (IOException ioException) { - ioException.printStackTrace(); - } - String jsonContent = new String(fileBytes, StandardCharsets.UTF_8); - cWorld.setInfoData(gson.fromJson(jsonContent, WorldInfoData.class)); - } else { - cWorld.setInfoData(WorldInfoData.empty()); - } - } - } - - @Override - public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) { - CWorld cWorld = (CWorld) customCropsWorld; - World world = cWorld.getWorld(); - if (world == null) { - LogUtils.severe("Unexpected issue: World " + cWorld.getWorldName() + " unloaded before data loaded"); - return; - } - - long time1 = System.currentTimeMillis(); - // load lazy chunks firstly - CustomCropsChunk lazyChunk = cWorld.removeLazyChunkAt(chunkPos); - if (lazyChunk != null) { - CChunk cChunk = (CChunk) lazyChunk; - cChunk.resetUnloadedSeconds(); - cWorld.loadChunk(cChunk); - long time2 = System.currentTimeMillis(); - CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos + " from lazy chunks"); - return; - } - - // check if region is loaded, load if not loaded - RegionPos regionPos = RegionPos.getByChunkPos(chunkPos); - Optional optionalRegion = cWorld.getLoadedRegionAt(regionPos); - if (optionalRegion.isPresent()) { - CustomCropsRegion region = optionalRegion.get(); - byte[] bytes = region.getChunkBytes(chunkPos); - if (bytes != null) { - try { - DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(bytes)); - CChunk chunk = deserializeChunk(cWorld, dataStream); - dataStream.close(); - cWorld.loadChunk(chunk); - long time2 = System.currentTimeMillis(); - CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos + " from cached region"); - } catch (IOException e) { - LogUtils.severe("Failed to load CustomCrops data at " + chunkPos); - e.printStackTrace(); - region.removeChunk(chunkPos); - } - } - return; - } - - // if region file not exist, create one - File data = getRegionDataFilePath(world, regionPos); - if (!data.exists()) { - var region = new CRegion(cWorld, regionPos); - cWorld.loadRegion(region); - return; - } - - // load region from local files - try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(data))) { - DataInputStream dataStream = new DataInputStream(bis); - CRegion region = deserializeRegion(cWorld, dataStream, regionPos); - dataStream.close(); - cWorld.loadRegion(region); - byte[] bytes = region.getChunkBytes(chunkPos); - if (bytes != null) { - try { - DataInputStream chunkStream = new DataInputStream(new ByteArrayInputStream(bytes)); - CChunk chunk = deserializeChunk(cWorld, chunkStream); - chunkStream.close(); - cWorld.loadChunk(chunk); - long time2 = System.currentTimeMillis(); - CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos); - } catch (IOException e) { - LogUtils.severe("Failed to load CustomCrops data at " + chunkPos + ". Deleting corrupted chunk."); - e.printStackTrace(); - region.removeChunk(chunkPos); - } - } else { - long time2 = System.currentTimeMillis(); - CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load region " + regionPos); - } - } catch (Exception e) { - LogUtils.severe("Failed to load CustomCrops region data at " + chunkPos + ". Deleting corrupted region."); - e.printStackTrace(); - data.delete(); - } - } - - @Override - public void unloadChunkData(CustomCropsWorld ccWorld, ChunkPos chunkPos) { - CWorld cWorld = (CWorld) ccWorld; - World world = cWorld.getWorld(); - if (world == null) { - LogUtils.severe("Unexpected issue: World " + cWorld.getWorldName() + " unloaded before data loaded"); - return; - } - - cWorld.unloadChunk(chunkPos); - } - - @Override - public void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk) { - CustomCropsRegion customCropsRegion = customCropsChunk.getCustomCropsRegion(); - if (customCropsRegion == null) { - LogUtils.severe(customCropsChunk.getChunkPos().toString() + " unloaded before chunk saving"); - return; - } - SerializableChunk serializableChunk = toSerializableChunk((CChunk) customCropsChunk); - if (serializableChunk.canPrune()) { - customCropsRegion.removeChunk(customCropsChunk.getChunkPos()); - } else { - customCropsRegion.saveChunk(customCropsChunk.getChunkPos(), serialize(serializableChunk)); - } - } - - @Override - public void saveRegion(CustomCropsRegion customCropsRegion) { - File file = getRegionDataFilePath(customCropsRegion.getCustomCropsWorld().getWorld(), customCropsRegion.getRegionPos()); - long time1 = System.currentTimeMillis(); - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) { - bos.write(serialize(customCropsRegion)); - long time2 = System.currentTimeMillis(); - CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to save region " + customCropsRegion.getRegionPos()); - } catch (IOException e) { - LogUtils.severe("Failed to save CustomCrops region data." + customCropsRegion.getRegionPos()); - e.printStackTrace(); - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) - public void onWorldLoad(WorldLoadEvent event) { - if (worldManager.isMechanicEnabled(event.getWorld())) { - worldManager.loadWorld(event.getWorld()); - } - } - - @EventHandler (ignoreCancelled = true, priority = EventPriority.LOW) - public void onWorldUnload(WorldUnloadEvent event) { - if (worldManager.isMechanicEnabled(event.getWorld())) - worldManager.unloadWorld(event.getWorld()); - } - - @EventHandler (ignoreCancelled = true) - public void onWorldSave(WorldSaveEvent event) { - World world = event.getWorld(); - worldManager.getCustomCropsWorld(world).ifPresent(CustomCropsWorld::save); - } - - @Deprecated - private String getChunkDataFile(ChunkPos chunkPos) { - return chunkPos.x() + "," + chunkPos.z() + ".ccd"; - } - - private String getRegionDataFile(RegionPos regionPos) { - return "r." + regionPos.x() + "." + regionPos.z() + ".mcc"; - } - - @Deprecated - private File getChunkDataFilePath(World world, ChunkPos chunkPos) { - if (worldFolder.isEmpty()) { - return new File(world.getWorldFolder(), "customcrops" + File.separator + getChunkDataFile(chunkPos)); - } else { - return new File(worldFolder, world.getName() + File.separator + "customcrops" + File.separator + getChunkDataFile(chunkPos)); - } - } - - private File getRegionDataFilePath(World world, RegionPos regionPos) { - if (worldFolder.isEmpty()) { - return new File(world.getWorldFolder(), "customcrops" + File.separator + getRegionDataFile(regionPos)); - } else { - return new File(worldFolder, world.getName() + File.separator + "customcrops" + File.separator + getRegionDataFile(regionPos)); - } - } - - private File getWorldFolder(World world) { - if (worldFolder.isEmpty()) { - return new File(world.getWorldFolder(), "customcrops"); - } else { - return new File(worldFolder, world.getName() + File.separator + "customcrops"); - } - } - - public void setWorldFolder(String folder) { - this.worldFolder = folder; - } - - public byte[] serialize(CustomCropsRegion region) { - ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); - DataOutputStream outStream = new DataOutputStream(outByteStream); - try { - outStream.writeByte(regionVersion); - outStream.writeInt(region.getRegionPos().x()); - outStream.writeInt(region.getRegionPos().z()); - Map map = region.getRegionDataToSave(); - outStream.writeInt(map.size()); - for (Map.Entry entry : map.entrySet()) { - outStream.writeInt(entry.getKey().x()); - outStream.writeInt(entry.getKey().z()); - byte[] dataArray = entry.getValue(); - outStream.writeInt(dataArray.length); - outStream.write(dataArray); - } - } catch (IOException e) { - e.printStackTrace(); - } - return outByteStream.toByteArray(); - } - - public CRegion deserializeRegion(CWorld world, DataInputStream dataStream, RegionPos pos) throws IOException { - int regionVersion = dataStream.readByte(); - int regionX = dataStream.readInt(); - int regionZ = dataStream.readInt(); - RegionPos regionPos = RegionPos.of(regionX, regionZ); - ConcurrentHashMap map = new ConcurrentHashMap<>(); - int chunkAmount = dataStream.readInt(); - for (int i = 0; i < chunkAmount; i++) { - int chunkX = dataStream.readInt(); - int chunkZ = dataStream.readInt(); - ChunkPos chunkPos = ChunkPos.of(chunkX, chunkZ); - byte[] chunkData = new byte[dataStream.readInt()]; - dataStream.read(chunkData); - map.put(chunkPos, chunkData); - } - return new CRegion(world, pos, map); - } - - public byte[] serialize(SerializableChunk serializableChunk) { - ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); - DataOutputStream outStream = new DataOutputStream(outByteStream); - try { - outStream.writeByte(chunkVersion); - byte[] serializedSections = serializeChunk(serializableChunk); - byte[] compressed = Zstd.compress(serializedSections); - outStream.writeInt(compressed.length); - outStream.writeInt(serializedSections.length); - outStream.write(compressed); - } catch (IOException e) { - e.printStackTrace(); - } - return outByteStream.toByteArray(); - } - - public CChunk deserializeChunk(CWorld world, DataInputStream dataStream) throws IOException { - int chunkVersion = dataStream.readByte(); - byte[] blockData = readCompressedBytes(dataStream); - return deserializeChunk(world, blockData); - } - - private CChunk deserializeChunk(CWorld cWorld, byte[] bytes) throws IOException { - String world = cWorld.getWorldName(); - DataInputStream chunkData = new DataInputStream(new ByteArrayInputStream(bytes)); - // read coordinate - int x = chunkData.readInt(); - int z = chunkData.readInt(); - ChunkPos coordinate = new ChunkPos(x, z); - // read loading info - int loadedSeconds = chunkData.readInt(); - long lastLoadedTime = chunkData.readLong(); - // read task queue - int tasksSize = chunkData.readInt(); - PriorityQueue queue = new PriorityQueue<>(Math.max(11, tasksSize)); - for (int i = 0; i < tasksSize; i++) { - int time = chunkData.readInt(); - BlockPos pos = new BlockPos(chunkData.readInt()); - queue.add(new TickTask(time, pos)); - } - // read ticked blocks - int tickedSize = chunkData.readInt(); - HashSet tickedSet = new HashSet<>(Math.max(11, tickedSize)); - for (int i = 0; i < tickedSize; i++) { - tickedSet.add(new BlockPos(chunkData.readInt())); - } - // read block data - ConcurrentHashMap sectionMap = new ConcurrentHashMap<>(); - int sections = chunkData.readInt(); - // read sections - for (int i = 0; i < sections; i++) { - ConcurrentHashMap blockMap = new ConcurrentHashMap<>(); - int sectionID = chunkData.readInt(); - byte[] sectionBytes = new byte[chunkData.readInt()]; - chunkData.read(sectionBytes); - DataInputStream sectionData = new DataInputStream(new ByteArrayInputStream(sectionBytes)); - int blockAmount = sectionData.readInt(); - // read blocks - for (int j = 0; j < blockAmount; j++){ - byte[] blockData = new byte[sectionData.readInt()]; - sectionData.read(blockData); - CompoundMap block = readCompound(blockData).getValue(); - String type = (String) block.get("type").getValue(); - CompoundMap data = (CompoundMap) block.get("data").getValue(); - switch (type) { - case "CROP" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemoryCrop(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - case "POT" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemoryPot(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - case "SPRINKLER" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemorySprinkler(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - case "SCARECROW" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemoryScarecrow(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - case "GREENHOUSE" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemoryGlass(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - } - } - CSection cSection = new CSection(sectionID, blockMap); - sectionMap.put(sectionID, cSection); - } - - return new CChunk(cWorld, coordinate, loadedSeconds, lastLoadedTime, sectionMap, queue, tickedSet); - } - - private byte[] serializeChunk(SerializableChunk chunk) throws IOException { - ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384); - DataOutputStream outStream = new DataOutputStream(outByteStream); - outStream.writeInt(chunk.getX()); - outStream.writeInt(chunk.getZ()); - outStream.writeInt(chunk.getLoadedSeconds()); - outStream.writeLong(chunk.getLastLoadedTime()); - // write queue - int[] queue = chunk.getQueuedTasks(); - outStream.writeInt(queue.length / 2); - for (int i : queue) { - outStream.writeInt(i); - } - // write ticked blocks - int[] tickedSet = chunk.getTicked(); - outStream.writeInt(tickedSet.length); - for (int i : tickedSet) { - outStream.writeInt(i); - } - // write block data - List sectionsToSave = chunk.getSections(); - outStream.writeInt(sectionsToSave.size()); - for (SerializableSection section : sectionsToSave) { - outStream.writeInt(section.getSectionID()); - byte[] blockData = serializeBlocks(section.getBlocks()); - outStream.writeInt(blockData.length); - outStream.write(blockData); - } - return outByteStream.toByteArray(); - } - - private byte[] serializeBlocks(Collection blocks) throws IOException { - ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384); - DataOutputStream outStream = new DataOutputStream(outByteStream); - outStream.writeInt(blocks.size()); - for (CompoundTag block : blocks) { - byte[] blockData = serializeCompoundTag(block); - outStream.writeInt(blockData.length); - outStream.write(blockData); - } - return outByteStream.toByteArray(); - } - - private byte[] readCompressedBytes(DataInputStream dataStream) throws IOException { - int compressedLength = dataStream.readInt(); - int decompressedLength = dataStream.readInt(); - byte[] compressedData = new byte[compressedLength]; - byte[] decompressedData = new byte[decompressedLength]; - - dataStream.read(compressedData); - Zstd.decompress(decompressedData, compressedData); - return decompressedData; - } - - public SerializableChunk toSerializableChunk(CChunk chunk) { - ChunkPos chunkPos = chunk.getChunkPos(); - return new SerializableChunk( - chunkPos.x(), - chunkPos.z(), - chunk.getLoadedSeconds(), - chunk.getLastLoadedTime(), - Arrays.stream(chunk.getSectionsForSerialization()).map(this::toSerializableSection).toList(), - queueToIntArray(chunk.getQueue()), - tickedBlocksToArray(chunk.getTickedBlocks()) - ); - } - - private int[] tickedBlocksToArray(Set set) { - int[] ticked = new int[set.size()]; - int i = 0; - for (BlockPos pos : set) { - ticked[i] = pos.getPosition(); - i++; - } - return ticked; - } - - private int[] queueToIntArray(PriorityQueue queue) { - int size = queue.size() * 2; - int[] tasks = new int[size]; - int i = 0; - for (TickTask task : queue) { - tasks[i * 2] = task.getTime(); - tasks[i * 2 + 1] = task.getChunkPos().getPosition(); - i++; - } - return tasks; - } - - private SerializableSection toSerializableSection(CSection section) { - return new SerializableSection(section.getSectionID(), toCompoundTags(section.getBlockMap())); - } - - private List toCompoundTags(Map blocks) { - ArrayList tags = new ArrayList<>(blocks.size()); - Map> blockToPosMap = new HashMap<>(); - for (Map.Entry entry : blocks.entrySet()) { - BlockPos coordinate = entry.getKey(); - CustomCropsBlock block = entry.getValue(); - List coordinates = blockToPosMap.computeIfAbsent(block, k -> new ArrayList<>()); - coordinates.add(coordinate.getPosition()); - } - for (Map.Entry> entry : blockToPosMap.entrySet()) { - tags.add(new CompoundTag("", toCompoundMap(entry.getKey(), entry.getValue()))); - } - return tags; - } - - private CompoundMap toCompoundMap(CustomCropsBlock block, List pos) { - CompoundMap map = new CompoundMap(); - int[] result = new int[pos.size()]; - for (int i = 0; i < pos.size(); i++) { - result[i] = pos.get(i); - } - map.put(new StringTag("type", block.getType().name())); - map.put(new IntArrayTag("pos", result)); - map.put(new CompoundTag("data", block.getCompoundMap().getOriginalMap())); - return map; - } - - private CompoundTag readCompound(byte[] bytes) throws IOException { - if (bytes.length == 0) - return null; - NBTInputStream nbtInputStream = new NBTInputStream( - new ByteArrayInputStream(bytes), - NBTInputStream.NO_COMPRESSION, - ByteOrder.BIG_ENDIAN - ); - return (CompoundTag) nbtInputStream.readTag(); - } - - private byte[] serializeCompoundTag(CompoundTag tag) throws IOException { - if (tag == null || tag.getValue().isEmpty()) - return new byte[0]; - ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); - NBTOutputStream outStream = new NBTOutputStream( - outByteStream, - NBTInputStream.NO_COMPRESSION, - ByteOrder.BIG_ENDIAN - ); - outStream.writeTag(tag); - return outByteStream.toByteArray(); - } - - public boolean convertWorldFromV33toV34(@Nullable CWorld cWorld, World world) { - // handle legacy files - File leagcyFile = new File(world.getWorldFolder(), "customcrops" + File.separator + "data.yml"); - if (leagcyFile.exists()) { - // read date and season - YamlConfiguration data = YamlConfiguration.loadConfiguration(leagcyFile); - try { - Season season = Season.valueOf(data.getString("season", "SPRING")); - if (cWorld != null) - cWorld.setInfoData(new WorldInfoData(season, data.getInt("date", 1))); - world.getPersistentDataContainer().set(key, PersistentDataType.STRING, - gson.toJson(new WorldInfoData(season, data.getInt("date", 1)))); - } catch (Exception e) { - if (cWorld != null) - cWorld.setInfoData(WorldInfoData.empty()); - } - // delete the file - leagcyFile.delete(); - new File(world.getWorldFolder(), "customcrops" + File.separator + "corrupted.yml").delete(); - - // read chunks - File folder = new File(world.getWorldFolder(), "customcrops" + File.separator + "chunks"); - if (!folder.exists()) return false; - LogUtils.warn("Converting chunks for world " + world.getName() + " from 3.3 to 3.4... This might take some time."); - File[] data_files = folder.listFiles(); - if (data_files == null) return false; - - HashMap regionHashMap = new HashMap<>(); - - for (File file : data_files) { - ChunkPos chunkPos = ChunkPos.getByString(file.getName().substring(0, file.getName().length() - 7)); - try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) { - CCChunk chunk = (CCChunk) ois.readObject(); - CChunk cChunk = new CChunk(cWorld, chunkPos); - for (net.momirealms.customcrops.api.object.world.SimpleLocation legacyLocation : chunk.getGreenhouseSet()) { - SimpleLocation simpleLocation = new SimpleLocation(legacyLocation.getWorldName(), legacyLocation.getX(), legacyLocation.getY(), legacyLocation.getZ()); - cChunk.addGlassAt(new MemoryGlass(simpleLocation), simpleLocation); - } - for (net.momirealms.customcrops.api.object.world.SimpleLocation legacyLocation : chunk.getScarecrowSet()) { - SimpleLocation simpleLocation = new SimpleLocation(legacyLocation.getWorldName(), legacyLocation.getX(), legacyLocation.getY(), legacyLocation.getZ()); - cChunk.addScarecrowAt(new MemoryScarecrow(simpleLocation), simpleLocation); - } - for (Map.Entry entry : chunk.getGrowingCropMap().entrySet()) { - net.momirealms.customcrops.api.object.world.SimpleLocation legacyLocation = entry.getKey(); - SimpleLocation simpleLocation = new SimpleLocation(legacyLocation.getWorldName(), legacyLocation.getX(), legacyLocation.getY(), legacyLocation.getZ()); - cChunk.addCropAt(new MemoryCrop(simpleLocation, entry.getValue().getKey(), entry.getValue().getPoints()), simpleLocation); - } - for (Map.Entry entry : chunk.getSprinklerMap().entrySet()) { - net.momirealms.customcrops.api.object.world.SimpleLocation legacyLocation = entry.getKey(); - SimpleLocation simpleLocation = new SimpleLocation(legacyLocation.getWorldName(), legacyLocation.getX(), legacyLocation.getY(), legacyLocation.getZ()); - cChunk.addSprinklerAt(new MemorySprinkler(simpleLocation, entry.getValue().getKey(), entry.getValue().getWater()), simpleLocation); - } - for (Map.Entry entry : chunk.getPotMap().entrySet()) { - net.momirealms.customcrops.api.object.world.SimpleLocation legacyLocation = entry.getKey(); - SimpleLocation simpleLocation = new SimpleLocation(legacyLocation.getWorldName(), legacyLocation.getX(), legacyLocation.getY(), legacyLocation.getZ()); - Fertilizer fertilizer = entry.getValue().getFertilizer(); - cChunk.addPotAt(new MemoryPot(simpleLocation, entry.getValue().getKey(), entry.getValue().getWater(), fertilizer == null ? "" : fertilizer.getKey(), fertilizer == null ? 0 : fertilizer.getTimes()), simpleLocation); - } - CustomCropsRegion region = regionHashMap.get(chunkPos.getRegionPos()); - if (region == null) { - region = new CRegion(cWorld, chunkPos.getRegionPos()); - regionHashMap.put(chunkPos.getRegionPos(), region); - } - region.saveChunk(chunkPos, serialize(toSerializableChunk(cChunk))); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - LogUtils.info("Error at " + file.getAbsolutePath()); - } - } - - for (CustomCropsRegion region : regionHashMap.values()) { - saveRegion(region); - } - LogUtils.info("Successfully converted chunks for world: " + world.getName()); - return true; - } - return false; - } - - public void convertWorldFromV342toV343(@Nullable CWorld cWorld, World world) { - File folder = new File(world.getWorldFolder(), "customcrops"); - if (!folder.exists()) return; - LogUtils.warn("Converting chunks for world " + world.getName() + " from 3.4.2 to 3.4.3... This might take some time."); - File[] data_files = folder.listFiles(); - if (data_files == null) return; - HashMap regionHashMap = new HashMap<>(); - for (File file : data_files) { - String fileName = file.getName(); - if (fileName.endsWith(".ccd")) { - String chunkStr = file.getName().substring(0, fileName.length()-4); - ChunkPos chunkPos = ChunkPos.getByString(chunkStr); - try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) { - DataInputStream dataStream = new DataInputStream(bis); - byte[] chunkData = dataStream.readAllBytes(); - dataStream.close(); - CustomCropsRegion region = regionHashMap.get(chunkPos.getRegionPos()); - if (region == null) { - region = new CRegion(cWorld, chunkPos.getRegionPos()); - regionHashMap.put(chunkPos.getRegionPos(), region); - } - region.saveChunk(chunkPos, chunkData); - } catch (IOException e) { - e.printStackTrace(); - } - file.delete(); - } - } - for (CustomCropsRegion region : regionHashMap.values()) { - saveRegion(region); - } - LogUtils.info("Successfully converted chunks for world: " + world.getName()); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java deleted file mode 100644 index 679d85917..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.adaptor; - -import com.flowpowered.nbt.*; -import com.infernalsuite.aswm.api.events.LoadSlimeWorldEvent; -import com.infernalsuite.aswm.api.world.SlimeWorld; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.WorldManager; -import net.momirealms.customcrops.api.mechanic.world.BlockPos; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsRegion; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; -import net.momirealms.customcrops.api.mechanic.world.level.WorldInfoData; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.world.*; -import net.momirealms.customcrops.mechanic.world.block.*; -import net.momirealms.customcrops.scheduler.task.TickTask; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.event.EventHandler; -import org.bukkit.plugin.Plugin; - -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.PriorityQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -public class SlimeWorldAdaptor extends BukkitWorldAdaptor { - - private final Function getSlimeWorldFunction; - - public SlimeWorldAdaptor(WorldManager worldManager, int version) { - super(worldManager); - try { - if (version == 1) { - Plugin plugin = Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); - Class slimeClass = Class.forName("com.infernalsuite.aswm.api.SlimePlugin"); - Method method = slimeClass.getMethod("getWorld", String.class); - this.getSlimeWorldFunction = (name) -> { - try { - return (SlimeWorld) method.invoke(plugin, name); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - }; - } else if (version == 2) { - Class apiClass = Class.forName("com.infernalsuite.aswm.api.AdvancedSlimePaperAPI"); - Object apiInstance = apiClass.getMethod("instance").invoke(null); - Method method = apiClass.getMethod("getLoadedWorld", String.class); - this.getSlimeWorldFunction = (name) -> { - try { - return (SlimeWorld) method.invoke(apiInstance, name); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - }; - } else { - throw new IllegalArgumentException("Unsupported version: " + version); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - private CompoundMap createOrGetDataMap(SlimeWorld world) { - Optional optionalCompoundTag = world.getExtraData().getAsCompoundTag("customcrops"); - CompoundMap ccDataMap; - if (optionalCompoundTag.isEmpty()) { - ccDataMap = new CompoundMap(); - world.getExtraData().getValue().put(new CompoundTag("customcrops", ccDataMap)); - } else { - ccDataMap = optionalCompoundTag.get().getValue(); - } - return ccDataMap; - } - - @EventHandler (ignoreCancelled = true) - public void onSlimeWorldLoad(LoadSlimeWorldEvent event) { - World world = Bukkit.getWorld(event.getSlimeWorld().getName()); - if (world == null) { - LogUtils.warn("Failed to load slime world because the bukkit world not loaded"); - return; - } - if (worldManager.isMechanicEnabled(world)) { - worldManager.loadWorld(world); - } - } - - @Override - public void saveInfoData(CustomCropsWorld customCropsWorld) { - SlimeWorld slimeWorld = getSlimeWorldFunction.apply(customCropsWorld.getWorldName()); - if (slimeWorld == null) { - super.saveInfoData(customCropsWorld); - return; - } - CompoundMap ccDataMap = createOrGetDataMap(slimeWorld); - ccDataMap.put(new StringTag("world-info", gson.toJson(customCropsWorld.getInfoData()))); - } - - @Override - public void unload(CustomCropsWorld customCropsWorld) { - SlimeWorld slimeWorld = getSlimeWorldFunction.apply(customCropsWorld.getWorldName()); - if (slimeWorld == null) { - super.unload(customCropsWorld); - return; - } - customCropsWorld.save(); - } - - @Override - public void init(CustomCropsWorld customCropsWorld) { - SlimeWorld slimeWorld = getSlimeWorldFunction.apply(customCropsWorld.getWorldName()); - if (slimeWorld == null) { - super.init(customCropsWorld); - return; - } - - CompoundMap ccDataMap = createOrGetDataMap(slimeWorld); - String json = Optional.ofNullable(ccDataMap.get("world-info")).map(tag -> tag.getAsStringTag().get().getValue()).orElse(null); - WorldInfoData data = (json == null || json.equals("null")) ? WorldInfoData.empty() : gson.fromJson(json, WorldInfoData.class); - customCropsWorld.setInfoData(data); - } - - @Override - public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) { - SlimeWorld slimeWorld = getSlimeWorldFunction.apply(customCropsWorld.getWorldName()); - if (slimeWorld == null) { - super.loadChunkData(customCropsWorld, chunkPos); - return; - } - - long time1 = System.currentTimeMillis(); - CWorld cWorld = (CWorld) customCropsWorld; - // load lazy chunks firstly - CustomCropsChunk lazyChunk = customCropsWorld.removeLazyChunkAt(chunkPos); - if (lazyChunk != null) { - CChunk cChunk = (CChunk) lazyChunk; - cChunk.resetUnloadedSeconds(); - cWorld.loadChunk(cChunk); - long time2 = System.currentTimeMillis(); - CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos + " from lazy chunks"); - return; - } - - CompoundMap ccDataMap = createOrGetDataMap(slimeWorld); - Tag chunkTag = ccDataMap.get(chunkPos.getAsString()); - if (chunkTag == null) { - return; - } - Optional chunkCompoundTag = chunkTag.getAsCompoundTag(); - if (chunkCompoundTag.isEmpty()) { - return; - } - - // load chunk from slime world - cWorld.loadChunk(tagToChunk(cWorld, chunkCompoundTag.get())); - long time2 = System.currentTimeMillis(); - CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos); - } - - @Override - public void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk) { - CustomCropsWorld customCropsWorld = customCropsChunk.getCustomCropsWorld(); - SlimeWorld slimeWorld = getSlimeWorld(customCropsWorld.getWorldName()); - if (slimeWorld == null) { - super.saveChunkToCachedRegion(customCropsChunk); - return; - } - - CompoundMap ccDataMap = createOrGetDataMap(slimeWorld); - - SerializableChunk serializableChunk = toSerializableChunk((CChunk) customCropsChunk); - if (Bukkit.isPrimaryThread()) { - if (serializableChunk.canPrune()) { - ccDataMap.remove(customCropsChunk.getChunkPos().getAsString()); - } else { - ccDataMap.put(chunkToTag(serializableChunk)); - } - } else { - CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { - if (serializableChunk.canPrune()) { - ccDataMap.remove(customCropsChunk.getChunkPos().getAsString()); - } else { - ccDataMap.put(chunkToTag(serializableChunk)); - } - }, null); - } - } - - @Override - public void saveRegion(CustomCropsRegion customCropsRegion) { - CustomCropsWorld customCropsWorld = customCropsRegion.getCustomCropsWorld(); - SlimeWorld slimeWorld = getSlimeWorld(customCropsWorld.getWorldName()); - if (slimeWorld == null) { - super.saveRegion(customCropsRegion); - return; - } - - // don't need to save region to slime world - } - - private SlimeWorld getSlimeWorld(String name) { - return getSlimeWorldFunction.apply(name); - } - - private CompoundTag chunkToTag(SerializableChunk serializableChunk) { - CompoundMap map = new CompoundMap(); - map.put(new IntTag("x", serializableChunk.getX())); - map.put(new IntTag("z", serializableChunk.getZ())); - map.put(new IntTag("loadedSeconds", serializableChunk.getLoadedSeconds())); - map.put(new LongTag("lastLoadedTime", serializableChunk.getLastLoadedTime())); - map.put(new IntArrayTag("queued", serializableChunk.getQueuedTasks())); - map.put(new IntArrayTag("ticked", serializableChunk.getTicked())); - CompoundMap sectionMap = new CompoundMap(); - for (SerializableSection section : serializableChunk.getSections()) { - sectionMap.put(new ListTag<>(String.valueOf(section.getSectionID()), TagType.TAG_COMPOUND, section.getBlocks())); - } - map.put(new CompoundTag("sections", sectionMap)); - return new CompoundTag(serializableChunk.getX() + "," + serializableChunk.getZ(), map); - } - - private CChunk tagToChunk(CWorld cWorld, CompoundTag tag) { - String world = cWorld.getWorldName(); - CompoundMap map = tag.getValue(); - int x = map.get("x").getAsIntTag().get().getValue(); - int z = map.get("z").getAsIntTag().get().getValue(); - ChunkPos coordinate = new ChunkPos(x, z); - int loadedSeconds = map.get("loadedSeconds").getAsIntTag().get().getValue(); - long lastLoadedTime = map.get("lastLoadedTime").getAsLongTag().get().getValue(); - int[] queued = map.get("queued").getAsIntArrayTag().get().getValue(); - int[] ticked = map.get("ticked").getAsIntArrayTag().get().getValue(); - - PriorityQueue queue = new PriorityQueue<>(Math.max(11, queued.length / 2)); - for (int i = 0, size = queued.length / 2; i < size; i++) { - BlockPos pos = new BlockPos(queued[2*i+1]); - queue.add(new TickTask(queued[2*i], pos)); - } - - HashSet tickedSet = new HashSet<>(Math.max(11, ticked.length)); - for (int tick : ticked) { - tickedSet.add(new BlockPos(tick)); - } - - ConcurrentHashMap sectionMap = new ConcurrentHashMap<>(); - CompoundMap sectionCompoundMap = map.get("sections").getAsCompoundTag().get().getValue(); - for (Map.Entry> entry : sectionCompoundMap.entrySet()) { - if (entry.getValue() instanceof ListTag listTag) { - int id = Integer.parseInt(entry.getKey()); - ConcurrentHashMap blockMap = new ConcurrentHashMap<>(); - ListTag blocks = (ListTag) listTag; - for (CompoundTag blockTag : blocks.getValue()) { - CompoundMap block = blockTag.getValue(); - String type = (String) block.get("type").getValue(); - CompoundMap data = (CompoundMap) block.get("data").getValue(); - switch (type) { - case "CROP" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemoryCrop(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - case "POT" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemoryPot(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - case "SPRINKLER" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemorySprinkler(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - case "SCARECROW" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemoryScarecrow(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - case "GLASS" -> { - for (int pos : (int[]) block.get("pos").getValue()) { - BlockPos blockPos = new BlockPos(pos); - blockMap.put(blockPos, new MemoryGlass(blockPos.getLocation(world, coordinate), new CompoundMap(data))); - } - } - } - } - sectionMap.put(id, new CSection(id, blockMap)); - } - } - - return new CChunk( - cWorld, - coordinate, - loadedSeconds, - lastLoadedTime, - sectionMap, - queue, - tickedSet - ); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java deleted file mode 100644 index 7a3dec945..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.block; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.IntTag; -import com.flowpowered.nbt.StringTag; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.condition.Condition; -import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; -import net.momirealms.customcrops.api.mechanic.item.Crop; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.item.ItemType; -import net.momirealms.customcrops.api.mechanic.item.fertilizer.SpeedGrow; -import net.momirealms.customcrops.api.mechanic.misc.CRotation; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -import java.util.Objects; -import java.util.Optional; - -public class MemoryCrop extends AbstractCustomCropsBlock implements WorldCrop { - - public MemoryCrop(SimpleLocation location, String key, int point) { - super(location, new CompoundMap()); - setData("point", new IntTag("point", point)); - setData("key", new StringTag("key", key)); - setData("tick", new IntTag("tick", 0)); - } - - public MemoryCrop(SimpleLocation location, CompoundMap properties) { - super(location, properties); - } - - @Override - public String getKey() { - return getData("key").getAsStringTag() - .map(StringTag::getValue) - .orElse(""); - } - - @Override - public int getPoint() { - return getData("point").getAsIntTag().map(IntTag::getValue).orElse(0); - } - - @Override - public void setPoint(int point) { - if (point < 0) return; - int max = getConfig().getMaxPoints(); - if (point > max) { - point = max; - } - setData("point", new IntTag("point", point)); - } - - @Override - public Crop getConfig() { - return CustomCropsPlugin.get().getItemManager().getCropByID(getKey()); - } - - @Override - public ItemType getType() { - return ItemType.CROP; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; - return Objects.equals(getCompoundMap(), that.getCompoundMap()); - } - - @Override - public int hashCode() { - return getKey().hashCode() + getPoint() * 17; - } - - @Override - public void tick(int interval, boolean offline) { - if (canTick(interval)) { - tick(offline); - } - } - - private void tick(boolean offline) { - Crop crop = getConfig(); - if (crop == null) { - LogUtils.warn("Found a crop without config at " + getLocation() + ". Try removing the data."); - CustomCropsPlugin.get().getWorldManager().removeCropAt(getLocation()); - return; - } - - SimpleLocation location = getLocation(); - Location bukkitLocation = location.getBukkitLocation(); - if (bukkitLocation == null) return; - - int previous = getPoint(); - - // check death conditions - for (DeathConditions deathConditions : crop.getDeathConditions()) { - for (Condition condition : deathConditions.getConditions()) { - if (condition.isConditionMet(this, offline)) { - CustomCropsPlugin.get().getScheduler().runTaskSyncLater(() -> { - CustomCropsPlugin.get().getWorldManager().removeCropAt(location); - CustomCropsPlugin.get().getItemManager().removeAnythingAt(bukkitLocation); - if (deathConditions.getDeathItem() != null) { - CustomCropsPlugin.get().getItemManager().placeItem(bukkitLocation, deathConditions.getItemCarrier(), deathConditions.getDeathItem()); - } - }, bukkitLocation, offline ? 0 : deathConditions.getDeathDelay()); - return; - } - } - } - - // don't check grow conditions if it's already ripe - if (previous >= crop.getMaxPoints()) { - return; - } - - // check grow conditions - for (Condition condition : crop.getGrowConditions().getConditions()) { - if (!condition.isConditionMet(this, offline)) { - return; - } - } - - // check pot & fertilizer - Optional pot = CustomCropsPlugin.get().getWorldManager().getPotAt(location.copy().add(0,-1,0)); - if (pot.isEmpty()) { - return; - } - - int point = 1; - Fertilizer fertilizer = pot.get().getFertilizer(); - if (fertilizer instanceof SpeedGrow speedGrow) { - point += speedGrow.getPointBonus(); - } - - int x = Math.min(previous + point, crop.getMaxPoints()); - setPoint(x); - String pre = crop.getStageItemByPoint(previous); - String after = crop.getStageItemByPoint(x); - - CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { - for (int i = previous + 1; i <= x; i++) { - Crop.Stage stage = crop.getStageByPoint(i); - if (stage != null) { - stage.trigger(ActionTrigger.GROW, new State(null, new ItemStack(Material.AIR), bukkitLocation)); - } - } - if (pre.equals(after)) return; - CRotation CRotation = CustomCropsPlugin.get().getItemManager().removeAnythingAt(bukkitLocation); - CustomCropsPlugin.get().getItemManager().placeItem(bukkitLocation, crop.getItemCarrier(), after, CRotation); - }, bukkitLocation); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java deleted file mode 100644 index 0d21d70f0..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.block; - -import com.flowpowered.nbt.CompoundMap; -import net.momirealms.customcrops.api.mechanic.item.ItemType; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.level.WorldGlass; - -import java.util.Objects; - -public class MemoryGlass extends AbstractCustomCropsBlock implements WorldGlass { - - public MemoryGlass(SimpleLocation location) { - super(location, new CompoundMap()); - } - - public MemoryGlass(SimpleLocation location, CompoundMap properties) { - super(location, properties); - } - - @Override - public ItemType getType() { - return ItemType.GREENHOUSE; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; - return Objects.equals(getCompoundMap(), that.getCompoundMap()); - } - - @Override - public int hashCode() { - return 1821739123; - } - - @Override - public void tick(int interval, boolean offline) { - - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java deleted file mode 100644 index 1bc313061..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.block; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.IntTag; -import com.flowpowered.nbt.StringTag; -import com.flowpowered.nbt.Tag; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -import net.momirealms.customcrops.api.mechanic.item.ItemType; -import net.momirealms.customcrops.api.mechanic.item.Pot; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.data.Waterlogged; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class MemoryPot extends AbstractCustomCropsBlock implements WorldPot { - - public MemoryPot(SimpleLocation location, CompoundMap compoundMap) { - super(location, compoundMap); - } - - public MemoryPot(SimpleLocation location, String key) { - super(location, new CompoundMap()); - setData("key", new StringTag("key", key)); - setData("water", new IntTag("water", 0)); - setData("fertilizer", new StringTag("fertilizer", "")); - setData("fertilizer-times", new IntTag("fertilizer-times", 0)); - } - - public MemoryPot(SimpleLocation location, String key, int water, String fertilizer, int fertilizerTimes) { - super(location, new CompoundMap()); - setData("key", new StringTag("key", key)); - setData("water", new IntTag("water", water)); - setData("fertilizer", new StringTag("fertilizer", fertilizer)); - setData("fertilizer-times", new IntTag("fertilizer-times", fertilizerTimes)); - } - - @Override - public String getKey() { - return getData("key").getAsStringTag() - .map(StringTag::getValue) - .orElse(""); - } - - @Override - public int getWater() { - return getData("water").getAsIntTag().map(IntTag::getValue).orElse(0); - } - - @Override - public void setWater(int water) { - if (water < 0) return; - int max = getConfig().getStorage(); - if (water > max) { - water = max; - } - setData("water", new IntTag("water", water)); - } - - @Override - public Fertilizer getFertilizer() { - Tag tag = getData("fertilizer"); - if (tag == null) return null; - return tag.getAsStringTag() - .map(strTag -> { - String key = strTag.getValue(); - return CustomCropsPlugin.get().getItemManager().getFertilizerByID(key); - }) - .orElse(null); - } - - @Override - public void setFertilizer(@NotNull Fertilizer fertilizer) { - setData("fertilizer", new StringTag("fertilizer", fertilizer.getKey())); - setData("fertilizer-times", new IntTag("fertilizer-times", fertilizer.getTimes())); - } - - @Override - public void removeFertilizer() { - setData("fertilizer", new StringTag("fertilizer", "")); - setData("fertilizer-times", new IntTag("fertilizer-times", 0)); - } - - @Override - public int getFertilizerTimes() { - return getData("fertilizer-times").getAsIntTag().map(IntTag::getValue).orElse(0); - } - - @Override - public void setFertilizerTimes(int times) { - setData("fertilizer-times", new IntTag("fertilizer-times", times)); - } - - @Override - public Pot getConfig() { - return CustomCropsPlugin.get().getItemManager().getPotByID(getKey()); - } - - @Override - public void tickWater() { - Pot pot = getConfig(); - if (pot == null) { - LogUtils.warn("Found a pot without config at " + getLocation() + ". Try removing the data."); - CustomCropsPlugin.get().getWorldManager().removePotAt(getLocation()); - return; - } - SimpleLocation location = getLocation(); - Location bukkitLocation = location.getBukkitLocation(); - World world = location.getBukkitWorld(); - if (world == null || bukkitLocation == null) return; - - if (pot.isRainDropAccepted()) { - if (world.hasStorm() || (!world.isClearWeather() && !world.isThundering())) { - double temperature = world.getTemperature(location.getX(), location.getY(), location.getZ()); - if (temperature > 0.15 && temperature < 0.85) { - Block highest = world.getHighestBlockAt(location.getX(), location.getZ()); - if (highest.getLocation().getY() == location.getY()) { - addWaterToPot(pot, location); - return; - } - } - } - } - if (pot.isNearbyWaterAccepted()) { - for (int i = -4; i <= 4; i++) { - for (int j = -4; j <= 4; j++) { - for (int k : new int[]{0, 1}) { - Block block = bukkitLocation.clone().add(i,k,j).getBlock(); - if (block.getType() == Material.WATER || (block.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged())) { - addWaterToPot(pot, location); - return; - } - } - } - } - } - } - - private void addWaterToPot(Pot pot, SimpleLocation location) { - int previous = getWater(); - if (previous >= pot.getStorage()) return; - setWater(Math.min(previous + 1, pot.getStorage())); - if (previous == 0) { - CustomCropsPlugin.get().getScheduler().runTaskSync(() -> CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, true, getFertilizer()), location.getBukkitLocation()); - } - } - - @Override - public ItemType getType() { - return ItemType.POT; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; - return Objects.equals(getCompoundMap(), that.getCompoundMap()); - } - - @Override - public int hashCode() { - return getKey().hashCode() + getWater() * 17; - } - - @Override - public void tick(int interval, boolean offline) { - if (canTick(interval)) { - tick(); - } - } - - // if the tick is triggered by offline growth - private void tick() { - Pot pot = getConfig(); - if (pot == null) { - LogUtils.warn("Found a pot without config at " + getLocation() + ". Try removing the data."); - CustomCropsPlugin.get().getWorldManager().removePotAt(getLocation()); - return; - } - - boolean loseFertilizer = false; - boolean loseWater = false; - int times = getFertilizerTimes(); - if (times > 0) { - if (--times <= 0) { - removeFertilizer(); - loseFertilizer = true; - } else { - setFertilizerTimes(times); - } - } - - int water = getWater(); - - SimpleLocation location = getLocation(); - Location bukkitLocation = location.getBukkitLocation(); - if (bukkitLocation == null) return; - World world = bukkitLocation.getWorld(); - - outer: { - if (water > 0) { - if (pot.isRainDropAccepted()) { - if (world.hasStorm() || (!world.isClearWeather() && !world.isThundering())) { - double temperature = world.getTemperature(location.getX(), location.getY(), location.getZ()); - if (temperature > 0.15 && temperature < 0.85) { - Block highest = world.getHighestBlockAt(location.getX(), location.getZ()); - if (highest.getLocation().getY() == location.getY()) { - break outer; - } - } - } - } - - if (pot.isNearbyWaterAccepted()) { - for (int i = -4; i <= 4; i++) { - for (int j = -4; j <= 4; j++) { - for (int k : new int[]{0, 1}) { - Block block = bukkitLocation.clone().add(i,k,j).getBlock(); - if (block.getType() == Material.WATER || (block.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged())) { - break outer; - } - } - } - } - } - - if (--water <= 0) { - loseWater = true; - } - setWater(water); - } - } - - if (loseFertilizer || loseWater) { - CustomCropsPlugin.get().getScheduler().runTaskSync(() -> - CustomCropsPlugin.get().getItemManager() - .updatePotState( - bukkitLocation, - pot, - getWater() > 0, - getFertilizer() - ), bukkitLocation - ); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java deleted file mode 100644 index 001192255..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.block; - -import com.flowpowered.nbt.CompoundMap; -import net.momirealms.customcrops.api.mechanic.item.ItemType; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.level.WorldScarecrow; - -import java.util.Objects; - -public class MemoryScarecrow extends AbstractCustomCropsBlock implements WorldScarecrow { - - public MemoryScarecrow(SimpleLocation location) { - super(location, new CompoundMap()); - } - - public MemoryScarecrow(SimpleLocation location, CompoundMap properties) { - super(location, properties); - } - - @Override - public ItemType getType() { - return ItemType.SCARECROW; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; - return Objects.equals(getCompoundMap(), that.getCompoundMap()); - } - - @Override - public int hashCode() { - return 28371283; - } - - @Override - public void tick(int interval, boolean offline) { - - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java deleted file mode 100644 index ba9cac0bf..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.mechanic.world.block; - -import com.flowpowered.nbt.CompoundMap; -import com.flowpowered.nbt.IntTag; -import com.flowpowered.nbt.StringTag; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.item.ItemType; -import net.momirealms.customcrops.api.mechanic.item.Pot; -import net.momirealms.customcrops.api.mechanic.item.Sprinkler; -import net.momirealms.customcrops.api.mechanic.requirement.State; -import net.momirealms.customcrops.api.mechanic.world.ChunkPos; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; -import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; -import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; -import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.inventory.ItemStack; - -import java.util.*; - -public class MemorySprinkler extends AbstractCustomCropsBlock implements WorldSprinkler { - - public MemorySprinkler(SimpleLocation location, CompoundMap compoundMap) { - super(location, compoundMap); - } - - public MemorySprinkler(SimpleLocation location, String key, int water) { - super(location, new CompoundMap()); - setData("water", new IntTag("water", water)); - setData("key", new StringTag("key", key)); - } - - @Override - public int getWater() { - return getData("water").getAsIntTag().map(IntTag::getValue).orElse(0); - } - - @Override - public void setWater(int water) { - if (water < 0) return; - int max = getConfig().getStorage(); - if (water > max) { - water = max; - } - setData("water", new IntTag("water", water)); - } - - @Override - public String getKey() { - return getData("key").getAsStringTag() - .map(StringTag::getValue) - .orElse(""); - } - - @Override - public Sprinkler getConfig() { - return CustomCropsPlugin.get().getItemManager().getSprinklerByID(getKey()); - } - - @Override - public ItemType getType() { - return ItemType.SPRINKLER; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; - return Objects.equals(getCompoundMap(), that.getCompoundMap()); - } - - @Override - public int hashCode() { - return getKey().hashCode() + getWater() * 17; - } - - @Override - public void tick(int interval, boolean offline) { - if (canTick(interval)) { - tick(offline); - } - } - - // if the tick is triggered by offline growth - private void tick(boolean offline) { - Sprinkler sprinkler = getConfig(); - if (sprinkler == null) { - LogUtils.warn("Found a sprinkler without config at " + getLocation() + ". Try removing the data."); - CustomCropsPlugin.get().getWorldManager().removeSprinklerAt(getLocation()); - return; - } - - SimpleLocation location = getLocation(); - boolean updateState; - if (!sprinkler.isInfinite()) { - int water = getWater(); - if (water <= 0) { - return; - } - setWater(--water); - updateState = water == 0; - } else { - updateState = false; - } - - Location bukkitLocation = location.getBukkitLocation(); - if (bukkitLocation == null) return; - CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { - State state = new State(null, new ItemStack(Material.AIR), bukkitLocation); - if (offline) state.setArg("{offline}", "true"); - sprinkler.trigger(ActionTrigger.WORK, state); - if (updateState && sprinkler.get3DItemWithWater() != null) { - CustomCropsPlugin.get().getItemManager().removeAnythingAt(bukkitLocation); - CustomCropsPlugin.get().getItemManager().placeItem(bukkitLocation, sprinkler.getItemCarrier(), sprinkler.get3DItemID()); - } - }, bukkitLocation); - - int range = sprinkler.getRange(); - HashMap> map = new HashMap<>(); - for (int i = -range; i <= range; i++) { - for (int j = -range; j <= range; j++) { - for (int k : new int[]{-1,0}) { - SimpleLocation potLocation = location.copy().add(i,k,j); - var cPos = potLocation.getChunkPos(); - var list = map.computeIfAbsent(cPos, key -> new ArrayList<>()); - list.add(potLocation); - } - } - } - - CustomCropsWorld world = CustomCropsPlugin.get().getWorldManager().getCustomCropsWorld(location.getWorldName()).get(); - World bkWorld = world.getWorld(); - for (Map.Entry> entry : map.entrySet()) { - var chunkPos = entry.getKey(); - CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { - // load the chunk firstly to load CustomCrops data - bkWorld.getChunkAt(chunkPos.x(), chunkPos.z()); - for (SimpleLocation potLocation : entry.getValue()) { - Optional pot = world.getPotAt(potLocation); - if (pot.isPresent()) { - WorldPot worldPot = pot.get(); - if (sprinkler.getPotWhitelist().contains(worldPot.getKey())) { - Pot potConfig = worldPot.getConfig(); - if (potConfig != null) { - int current = worldPot.getWater(); - if (current >= potConfig.getStorage()) { - continue; - } - worldPot.setWater(current + sprinkler.getWater()); - if (current == 0) { - CustomCropsPlugin.get().getItemManager().updatePotState(potLocation.getBukkitLocation(), potConfig, true, worldPot.getFertilizer()); - } - } - } - } - } - }, bkWorld, chunkPos.x(), chunkPos.z()); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/BukkitSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/BukkitSchedulerImpl.java deleted file mode 100644 index 76774f37e..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/scheduler/BukkitSchedulerImpl.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.scheduler; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.scheduler.CancellableTask; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.scheduler.BukkitTask; - -public class BukkitSchedulerImpl implements SyncScheduler { - - private final CustomCropsPlugin plugin; - - public BukkitSchedulerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - } - - @Override - public void runSyncTask(Runnable runnable, Location location) { - if (Bukkit.isPrimaryThread()) - runnable.run(); - else - Bukkit.getScheduler().runTask(plugin, runnable); - } - - @Override - public void runSyncTask(Runnable runnable, World world, int x, int z) { - runSyncTask(runnable, null); - } - - @Override - public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) { - return new BukkitCancellableTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period)); - } - - @Override - public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { - if (delay == 0) { - if (Bukkit.isPrimaryThread()) runnable.run(); - else Bukkit.getScheduler().runTask(plugin, runnable); - return new BukkitCancellableTask(null); - } - return new BukkitCancellableTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay)); - } - - public static class BukkitCancellableTask implements CancellableTask { - - private final BukkitTask bukkitTask; - - public BukkitCancellableTask(BukkitTask bukkitTask) { - this.bukkitTask = bukkitTask; - } - - @Override - public void cancel() { - if (this.bukkitTask != null) - this.bukkitTask.cancel(); - } - - @Override - public boolean isCancelled() { - if (this.bukkitTask == null) return true; - return this.bukkitTask.isCancelled(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java deleted file mode 100644 index 61c787e56..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.scheduler; - -import io.papermc.paper.threadedregions.scheduler.ScheduledTask; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.scheduler.CancellableTask; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; - -public class FoliaSchedulerImpl implements SyncScheduler { - - private final CustomCropsPlugin plugin; - - public FoliaSchedulerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - } - - @Override - public void runSyncTask(Runnable runnable, Location location) { - if (location == null) { - Bukkit.getGlobalRegionScheduler().execute(plugin, runnable); - } else { - Bukkit.getRegionScheduler().execute(plugin, location, runnable); - } - } - - @Override - public void runSyncTask(Runnable runnable, World world, int x, int z) { - Bukkit.getRegionScheduler().execute(plugin, world, x, z, runnable); - } - - @Override - public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) { - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, (scheduledTask -> runnable.run()), delay, period)); - } - return new FoliaCancellableTask(Bukkit.getRegionScheduler().runAtFixedRate(plugin, location, (scheduledTask -> runnable.run()), delay, period)); - } - - @Override - public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { - if (delay == 0) { - runSyncTask(runnable, location); - return new FoliaCancellableTask(null); - } - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, (scheduledTask -> runnable.run()), delay)); - } - return new FoliaCancellableTask(Bukkit.getRegionScheduler().runDelayed(plugin, location, (scheduledTask -> runnable.run()), delay)); - } - - public static class FoliaCancellableTask implements CancellableTask { - - private final ScheduledTask scheduledTask; - - public FoliaCancellableTask(ScheduledTask scheduledTask) { - this.scheduledTask = scheduledTask; - } - - @Override - public void cancel() { - this.scheduledTask.cancel(); - } - - @Override - public boolean isCancelled() { - return this.scheduledTask.isCancelled(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/SchedulerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/SchedulerImpl.java deleted file mode 100644 index a2022f531..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/scheduler/SchedulerImpl.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.scheduler; - -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.manager.ConfigManager; -import net.momirealms.customcrops.api.scheduler.CancellableTask; -import net.momirealms.customcrops.api.scheduler.Scheduler; -import net.momirealms.customcrops.api.util.LogUtils; -import org.bukkit.Location; -import org.bukkit.World; - -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * A scheduler implementation responsible for scheduling and managing tasks in a multi-threaded environment. - */ -public class SchedulerImpl implements Scheduler { - - private final SyncScheduler syncScheduler; - private final ScheduledThreadPoolExecutor schedule; - private final CustomCropsPlugin plugin; - - public SchedulerImpl(CustomCropsPlugin plugin) { - this.plugin = plugin; - this.syncScheduler = plugin.getVersionManager().hasRegionScheduler() ? - new FoliaSchedulerImpl(plugin) : new BukkitSchedulerImpl(plugin); - this.schedule = new ScheduledThreadPoolExecutor(1); - this.schedule.setMaximumPoolSize(1); - this.schedule.setKeepAliveTime(30, TimeUnit.SECONDS); - this.schedule.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); - } - - public void reload() { - try { - this.schedule.setMaximumPoolSize(ConfigManager.maximumPoolSize()); - this.schedule.setCorePoolSize(ConfigManager.corePoolSize()); - this.schedule.setKeepAliveTime(ConfigManager.keepAliveTime(), TimeUnit.SECONDS); - } catch (IllegalArgumentException e) { - LogUtils.warn("Failed to create thread pool. Please lower the corePoolSize in config.yml.", e); - } - } - - public void shutdown() { - if (this.schedule != null && !this.schedule.isShutdown()) - this.schedule.shutdown(); - } - - @Override - public void runTaskSync(Runnable runnable, Location location) { - this.syncScheduler.runSyncTask(runnable, location); - } - - @Override - public void runTaskSync(Runnable runnable, World world, int x, int z) { - this.syncScheduler.runSyncTask(runnable, world, x, z); - } - - @Override - public void runTaskAsync(Runnable runnable) { - try { - this.schedule.execute(runnable); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks) { - return this.syncScheduler.runTaskSyncTimer(runnable, location, delayTicks, periodTicks); - } - - @Override - public CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit) { - return new ScheduledTask(schedule.schedule(() -> { - try { - runnable.run(); - } catch (Exception e) { - e.printStackTrace(); - } - }, delay, timeUnit)); - } - - @Override - public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit timeUnit) { - return new ScheduledTask(schedule.schedule(() -> runTaskSync(runnable, location), delay, timeUnit)); - } - - @Override - public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks) { - return this.syncScheduler.runTaskSyncLater(runnable, location, delayTicks); - } - - @Override - public CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit) { - return new ScheduledTask(schedule.scheduleAtFixedRate(() -> { - try { - runnable.run(); - } catch (Exception e) { - e.printStackTrace(); - } - }, delay, period, timeUnit)); - } - - public static class ScheduledTask implements CancellableTask { - - private final ScheduledFuture scheduledFuture; - - public ScheduledTask(ScheduledFuture scheduledFuture) { - this.scheduledFuture = scheduledFuture; - } - - @Override - public void cancel() { - this.scheduledFuture.cancel(false); - } - - @Override - public boolean isCancelled() { - return this.scheduledFuture.isCancelled(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/SyncScheduler.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/SyncScheduler.java deleted file mode 100644 index f08621c88..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/scheduler/SyncScheduler.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.scheduler; - -import net.momirealms.customcrops.api.scheduler.CancellableTask; -import org.bukkit.Location; -import org.bukkit.World; - -public interface SyncScheduler { - - void runSyncTask(Runnable runnable, Location location); - - void runSyncTask(Runnable runnable, World world, int x, int z); - - CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks); - - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks); -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/ReplaceTask.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/ReplaceTask.java deleted file mode 100644 index 163fa77f7..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/ReplaceTask.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.scheduler.task; - -import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; -import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; - -public class ReplaceTask { - - private final SimpleLocation simpleLocation; - private final ItemCarrier carrier; - private final String id; - - public ReplaceTask(SimpleLocation simpleLocation, ItemCarrier carrier, String id) { - this.simpleLocation = simpleLocation; - this.carrier = carrier; - this.id = id; - } - - public SimpleLocation getSimpleLocation() { - return simpleLocation; - } - - public ItemCarrier getCarrier() { - return carrier; - } - - public String getID() { - return id; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java deleted file mode 100644 index f25b5ce22..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.util; - -import com.google.common.base.Preconditions; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.common.Pair; -import net.momirealms.customcrops.api.manager.PlaceholderManager; -import net.momirealms.customcrops.api.mechanic.action.Action; -import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; -import net.momirealms.customcrops.api.mechanic.condition.Condition; -import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; -import net.momirealms.customcrops.api.mechanic.item.BoneMeal; -import net.momirealms.customcrops.api.mechanic.item.FertilizerType; -import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; -import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; -import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; -import net.momirealms.customcrops.api.mechanic.misc.Value; -import net.momirealms.customcrops.api.mechanic.requirement.Requirement; -import net.momirealms.customcrops.api.mechanic.world.level.WorldSetting; -import net.momirealms.customcrops.api.util.LogUtils; -import net.momirealms.customcrops.mechanic.item.impl.CropConfig; -import net.momirealms.customcrops.mechanic.misc.value.ExpressionValue; -import net.momirealms.customcrops.mechanic.misc.value.PlainValue; -import net.objecthunter.exp4j.ExpressionBuilder; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.*; - -public class ConfigUtils { - - private ConfigUtils() {} - - public static YamlConfiguration getConfig(String file) { - File config = new File(CustomCropsPlugin.get().getDataFolder(), file); - if (!config.exists()) { - CustomCropsPlugin.get().saveResource(file, false); - addDefaultNamespace(config); - } - return YamlConfiguration.loadConfiguration(config); - } - - public static WorldSetting getWorldSettingFromSection(ConfigurationSection section) { - return WorldSetting.of( - section.getBoolean("enable", true), - section.getInt("min-tick-unit", 300), - getRandomTickModeByString(section.getString("crop.mode")), - section.getInt("crop.tick-interval", 1), - getRandomTickModeByString(section.getString("pot.mode")), - section.getInt("pot.tick-interval", 2), - getRandomTickModeByString(section.getString("sprinkler.mode")), - section.getInt("sprinkler.tick-interval", 2), - section.getBoolean("offline-tick.enable", false), - section.getInt("offline-tick.max-offline-seconds", 1200), - section.getBoolean("season.enable", false), - section.getBoolean("season.auto-alternation", false), - section.getInt("season.duration", 28), - section.getInt("crop.max-per-chunk", 128), - section.getInt("pot.max-per-chunk", -1), - section.getInt("sprinkler.max-per-chunk", 32), - section.getInt("random-tick-speed", 0) - ); - } - - public static boolean getRandomTickModeByString(String str) { - if (str == null) { - return false; - } - if (str.equalsIgnoreCase("RANDOM_TICK")) { - return true; - } - if (str.equalsIgnoreCase("SCHEDULED_TICK")) { - return false; - } - throw new IllegalArgumentException("Invalid mode found when loading world settings: " + str); - } - - public static boolean isVanillaItem(String item) { - char[] chars = item.toCharArray(); - for (char character : chars) { - if ((character < 65 || character > 90) && character != 95) { - return false; - } - } - return true; - } - - /** - * Converts an object into an ArrayList of strings. - * - * @param object The input object - * @return An ArrayList of strings - */ - @SuppressWarnings("unchecked") - public static ArrayList stringListArgs(Object object) { - ArrayList list = new ArrayList<>(); - if (object instanceof String member) { - list.add(member); - } else if (object instanceof List members) { - list.addAll((Collection) members); - } else if (object instanceof String[] strings) { - list.addAll(List.of(strings)); - } - return list; - } - - /** - * Converts an object into a double value. - * - * @param arg The input object - * @return A double value - */ - public static double getDoubleValue(Object arg) { - if (arg instanceof Double d) { - return d; - } else if (arg instanceof Integer i) { - return Double.valueOf(i); - } - return 0; - } - - /** - * Converts an object into a "value". - * - * @param arg int / double / expression - * @return Value - */ - public static Value getValue(Object arg) { - if (arg instanceof Integer i) { - return new PlainValue(i); - } else if (arg instanceof Double d) { - return new PlainValue(d); - } else if (arg instanceof String s) { - return new ExpressionValue(s); - } - throw new IllegalArgumentException("Illegal value type"); - } - - public static double getExpressionValue(Player player, String formula, Map vars) { - formula = PlaceholderManager.getInstance().parse(player, formula, vars); - return new ExpressionBuilder(formula).build().evaluate(); - } - - /** - * Splits a string into a pair of integers using the "~" delimiter. - * - * @param value The input string - * @return A Pair of integers - */ - public static Pair splitStringIntegerArgs(String value, String regex) { - String[] split = value.split(regex); - return Pair.of(Integer.parseInt(split[0]), Integer.parseInt(split[1])); - } - - public static List getFilesRecursively(File folder) { - List ymlFiles = new ArrayList<>(); - if (folder != null && folder.isDirectory()) { - File[] files = folder.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - ymlFiles.addAll(getFilesRecursively(file)); - } else if (file.getName().endsWith(".yml")) { - ymlFiles.add(file); - } - } - } - } - return ymlFiles; - } - - public static Action[] getActions(ConfigurationSection section) { - return CustomCropsPlugin.get().getActionManager().getActions(section); - } - - - public static HashMap getActionMap(ConfigurationSection section) { - HashMap map = new HashMap<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - try { - ActionTrigger trigger = ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); - map.put(trigger, getActions(innerSection)); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - } - } - return map; - } - - public static PositiveFillMethod[] getPositiveFillMethods(ConfigurationSection section) { - ArrayList methods = new ArrayList<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - PositiveFillMethod fillMethod = new PositiveFillMethod( - Preconditions.checkNotNull(innerSection.getString("target"), "fill-method target should not be null"), - innerSection.getInt("amount", 1), - getActions(innerSection.getConfigurationSection("actions")), - getRequirements(innerSection.getConfigurationSection("requirements")) - ); - methods.add(fillMethod); - } - } - } - return methods.toArray(new PositiveFillMethod[0]); - } - - public static PassiveFillMethod[] getPassiveFillMethods(ConfigurationSection section) { - ArrayList methods = new ArrayList<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - PassiveFillMethod fillMethod = new PassiveFillMethod( - Preconditions.checkNotNull(innerSection.getString("item"), "fill-method item should not be null"), - innerSection.getInt("item-amount", 1), - innerSection.getString("return"), - innerSection.getInt("return-amount", 1), - innerSection.getInt("amount", 1), - getActions(innerSection.getConfigurationSection("actions")), - getRequirements(innerSection.getConfigurationSection("requirements")) - ); - methods.add(fillMethod); - } - } - } - return methods.toArray(new PassiveFillMethod[0]); - } - - public static HashMap getInt2IntMap(ConfigurationSection section) { - HashMap map = new HashMap<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - try { - int i1 = Integer.parseInt(entry.getKey()); - if (entry.getValue() instanceof Integer i2) { - map.put(i1, i2); - } - } catch (NumberFormatException e) { - e.printStackTrace(); - } - } - } - return map; - } - - public static Requirement[] getRequirements(ConfigurationSection section) { - return CustomCropsPlugin.get().getRequirementManager().getRequirements(section, true); - } - - public static HashMap> getFertilizedPotMap(ConfigurationSection section) { - HashMap> map = new HashMap<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - FertilizerType type = switch (entry.getKey()) { - case "quality" -> FertilizerType.QUALITY; - case "yield-increase" -> FertilizerType.YIELD_INCREASE; - case "variation" -> FertilizerType.VARIATION; - case "soil-retain" -> FertilizerType.SOIL_RETAIN; - case "speed-grow" -> FertilizerType.SPEED_GROW; - default -> null; - }; - if (type != null) { - map.put(type, Pair.of( - Preconditions.checkNotNull(innerSection.getString("dry"), entry.getKey() + ".dry should not be null"), - Preconditions.checkNotNull(innerSection.getString("wet"), entry.getKey() + ".wet should not be null") - )); - } - } - } - } - return map; - } - - public static double[] getQualityRatio(String str) { - String[] split = str.split("/"); - double[] ratio = new double[split.length]; - double weightTotal = Arrays.stream(split).mapToInt(Integer::parseInt).sum(); - double temp = 0; - for (int i = 0; i < ratio.length; i++) { - temp += Integer.parseInt(split[i]); - ratio[i] = temp / weightTotal; - } - return ratio; - } - - public static List> getIntChancePair(ConfigurationSection section) { - ArrayList> pairs = new ArrayList<>(); - if (section != null) { - for (String point : section.getKeys(false)) { - Pair pair = new Pair<>(section.getDouble(point), Integer.parseInt(point)); - pairs.add(pair); - } - } - return pairs; - } - - public static BoneMeal[] getBoneMeals(ConfigurationSection section) { - ArrayList boneMeals = new ArrayList<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - BoneMeal boneMeal = new BoneMeal( - Preconditions.checkNotNull(innerSection.getString("item"), "Bone meal item can't be null"), - innerSection.getInt("item-amount",1), - innerSection.getString("return"), - innerSection.getInt("return-amount",1), - innerSection.getBoolean("dispenser",true), - getIntChancePair(innerSection.getConfigurationSection("chance")), - getActions(innerSection.getConfigurationSection("actions")) - ); - boneMeals.add(boneMeal); - } - } - } - return boneMeals.toArray(new BoneMeal[0]); - } - - public static DeathConditions[] getDeathConditions(ConfigurationSection section, ItemCarrier original) { - ArrayList conditions = new ArrayList<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - DeathConditions deathConditions = new DeathConditions( - getConditions(inner.getConfigurationSection("conditions")), - inner.getString("model"), - Optional.ofNullable(inner.getString("type")).map(ItemCarrier::valueOf).orElse(original), - inner.getInt("delay", 0) - ); - conditions.add(deathConditions); - } - } - } - return conditions.toArray(new DeathConditions[0]); - } - - public static Condition[] getConditions(ConfigurationSection section) { - return CustomCropsPlugin.get().getConditionManager().getConditions(section); - } - - public static HashMap getStageConfigs(ConfigurationSection section) { - HashMap map = new HashMap<>(); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - try { - int point = Integer.parseInt(entry.getKey()); - if (point < 0) { - LogUtils.warn(entry.getKey() + " is not a valid number"); - } else { - map.put(point, new CropConfig.CropStageConfig( - inner.getString("model"), - point, - inner.getDouble("hologram-offset-correction", 0d), - getActionMap(inner.getConfigurationSection("events")), - getRequirements(inner.getConfigurationSection("requirements.interact")), - getRequirements(inner.getConfigurationSection("requirements.break")) - )); - } - } catch (NumberFormatException e) { - LogUtils.warn(entry.getKey() + " is not a valid number"); - } - } - } - } - return map; - } - - public static void addDefaultNamespace(File file) { - boolean has = Bukkit.getPluginManager().getPlugin("ItemsAdder") != null; - String line; - StringBuilder sb = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { - while ((line = reader.readLine()) != null) { - sb.append(line).append(System.lineSeparator()); - } - } catch (IOException e) { - e.printStackTrace(); - } - try (BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { - String finalStr = sb.toString(); - if (!has) { - finalStr = finalStr.replace("CHORUS", "TRIPWIRE").replace("", ""); - } - writer.write(finalStr.replace("{0}", has ? "customcrops:" : "")); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java deleted file mode 100644 index 030c2a99a..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.util; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.wrappers.*; -import com.google.common.collect.Lists; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customcrops.api.manager.AdventureManager; -import net.momirealms.customcrops.api.manager.VersionManager; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; - -import java.util.*; - -public class FakeEntityUtils { - - public static WrappedDataWatcher createInvisibleDataWatcher() { - WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); - //wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, WrappedDataWatcher.Registry.get(Boolean.class)), false); - byte flag = 0x20; - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(0, WrappedDataWatcher.Registry.get(Byte.class)), flag); - return wrappedDataWatcher; - } - - public static PacketContainer getDestroyPacket(int id) { - PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); - destroyPacket.getIntLists().write(0, List.of(id)); - return destroyPacket; - } - - public static PacketContainer getSpawnPacket(int id, Location location, EntityType entityType) { - PacketContainer entityPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY); - entityPacket.getModifier().write(0, id); - entityPacket.getModifier().write(1, UUID.randomUUID()); - entityPacket.getEntityTypeModifier().write(0, entityType); - entityPacket.getDoubles().write(0, location.getX()); - entityPacket.getDoubles().write(1, location.getY()); - entityPacket.getDoubles().write(2, location.getZ()); - return entityPacket; - } - - public static PacketContainer getVanishArmorStandMetaPacket(int id) { - PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); - metaPacket.getIntegers().write(0, id); - if (VersionManager.isHigherThan1_19_R2()) { - WrappedDataWatcher wrappedDataWatcher = createInvisibleDataWatcher(); - setWrappedDataValue(metaPacket, wrappedDataWatcher); - } else { - metaPacket.getWatchableCollectionModifier().write(0, createInvisibleDataWatcher().getWatchableObjects()); - } - return metaPacket; - } - - public static PacketContainer getEquipPacket(int id, ItemStack itemStack) { - PacketContainer equipPacket = new PacketContainer(PacketType.Play.Server.ENTITY_EQUIPMENT); - equipPacket.getIntegers().write(0, id); - List> pairs = new ArrayList<>(); - pairs.add(new Pair<>(EnumWrappers.ItemSlot.HEAD, itemStack)); - equipPacket.getSlotStackPairLists().write(0, pairs); - return equipPacket; - } - - public static PacketContainer getTeleportPacket(int id, Location location, float yaw) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); - packet.getIntegers().write(0, id); - packet.getDoubles().write(0, location.getX()); - packet.getDoubles().write(1, location.getY()); - packet.getDoubles().write(2, location.getZ()); - packet.getBytes().write(0, (byte) (yaw * (128.0 / 180))); - return packet; - } - - public static PacketContainer getVanishArmorStandMetaPacket(int id, Component component) { - PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); - WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(2, WrappedDataWatcher.Registry.getChatComponentSerializer(true)), Optional.of(WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component)).getHandle())); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, WrappedDataWatcher.Registry.get(Boolean.class)), true); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(5, WrappedDataWatcher.Registry.get(Boolean.class)), true); - byte mask1 = 0x20; - byte mask2 = 0x01; - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(0, WrappedDataWatcher.Registry.get(Byte.class)), mask1); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(15, WrappedDataWatcher.Registry.get(Byte.class)), mask2); - metaPacket.getModifier().write(0, id); - if (VersionManager.isHigherThan1_19_R2()) { - setWrappedDataValue(metaPacket, wrappedDataWatcher); - } else { - metaPacket.getWatchableCollectionModifier().write(0, wrappedDataWatcher.getWatchableObjects()); - } - return metaPacket; - } - - private static void setWrappedDataValue(PacketContainer metaPacket, WrappedDataWatcher wrappedDataWatcher) { - List wrappedDataValueList = Lists.newArrayList(); - wrappedDataWatcher.getWatchableObjects().stream().filter(Objects::nonNull).forEach(entry -> { - final WrappedDataWatcher.WrappedDataWatcherObject dataWatcherObject = entry.getWatcherObject(); - wrappedDataValueList.add(new WrappedDataValue(dataWatcherObject.getIndex(), dataWatcherObject.getSerializer(), entry.getRawValue())); - }); - metaPacket.getDataValueCollectionModifier().write(0, wrappedDataValueList); - } - - public static PacketContainer get1_19_4TextDisplayMetaPacket(int id, Component component) { - PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); - metaPacket.getModifier().write(0, id); - WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(22, WrappedDataWatcher.Registry.getChatComponentSerializer(false)), WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component))); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(24, WrappedDataWatcher.Registry.get(Integer.class)), AdventureManager.getInstance().rgbaToDecimal("0,0,0,0")); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(14, WrappedDataWatcher.Registry.get(Byte.class)), (byte) 3); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(25, WrappedDataWatcher.Registry.get(Byte.class)), (byte) -1); - int mask = 0; - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(26, WrappedDataWatcher.Registry.get(Byte.class)), (byte) mask); - setWrappedDataValue(metaPacket, wrappedDataWatcher); - return metaPacket; - } - - public static PacketContainer get1_20_2TextDisplayMetaPacket(int id, Component component) { - PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); - metaPacket.getModifier().write(0, id); - WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(23, WrappedDataWatcher.Registry.getChatComponentSerializer(false)), WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component))); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(25, WrappedDataWatcher.Registry.get(Integer.class)), AdventureManager.getInstance().rgbaToDecimal("0,0,0,0")); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(15, WrappedDataWatcher.Registry.get(Byte.class)), (byte) 3); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(26, WrappedDataWatcher.Registry.get(Byte.class)), (byte) -1); - int mask = 0; - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(27, WrappedDataWatcher.Registry.get(Byte.class)), (byte) mask); - setWrappedDataValue(metaPacket, wrappedDataWatcher); - return metaPacket; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java deleted file mode 100644 index eb8e5042a..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.util; - -import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory; -import net.momirealms.customcrops.mechanic.item.factory.Item; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerItemDamageEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.bukkit.inventory.meta.ItemMeta; - -public class ItemUtils { - - public static void giveItem(Player player, ItemStack itemStack, int amount) { - PlayerInventory inventory = player.getInventory(); - ItemMeta meta = itemStack.getItemMeta(); - int maxStackSize = itemStack.getMaxStackSize(); - for (ItemStack other : inventory.getStorageContents()) { - if (other != null) { - if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { - if (other.getAmount() < maxStackSize) { - int delta = maxStackSize - other.getAmount(); - if (amount > delta) { - other.setAmount(maxStackSize); - amount -= delta; - } else { - other.setAmount(amount + other.getAmount()); - return; - } - } - } - } - } - if (amount > 0) { - for (ItemStack other : inventory.getStorageContents()) { - if (other == null) { - if (amount > maxStackSize) { - amount -= maxStackSize; - ItemStack cloned = itemStack.clone(); - cloned.setAmount(maxStackSize); - inventory.addItem(cloned); - } else { - ItemStack cloned = itemStack.clone(); - cloned.setAmount(amount); - inventory.addItem(cloned); - return; - } - } - } - } - if (amount > 0) { - for (int i = 0; i < amount / maxStackSize; i++) { - ItemStack cloned = itemStack.clone(); - cloned.setAmount(maxStackSize); - player.getWorld().dropItem(player.getLocation(), cloned); - } - int left = amount % maxStackSize; - if (left != 0) { - ItemStack cloned = itemStack.clone(); - cloned.setAmount(left); - player.getWorld().dropItem(player.getLocation(), cloned); - } - } - } - - public static void increaseDurability(ItemStack itemStack, int amount) { - if (itemStack == null || itemStack.getType() == Material.AIR) - return; - Item item = BukkitItemFactory.getInstance().wrap(itemStack.clone()); - int damage = Math.max(item.damage().orElse(0) - amount, 0); - item.damage(damage); - itemStack.setItemMeta(item.load().getItemMeta()); - } - - public static void decreaseDurability(Player player, ItemStack itemStack, int amount) { - if (itemStack == null || itemStack.getType() == Material.AIR) - return; - ItemMeta previousMeta = itemStack.getItemMeta().clone(); - PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(player, itemStack, amount, amount); - Bukkit.getPluginManager().callEvent(itemDamageEvent); - if (!itemStack.getItemMeta().equals(previousMeta) || itemDamageEvent.isCancelled()) { - return; - } - int unBreakingLevel = itemStack.getEnchantmentLevel(Enchantment.DURABILITY); - if (Math.random() > (double) 1 / (unBreakingLevel + 1)) { - return; - } - Item item = BukkitItemFactory.getInstance().wrap(itemStack.clone()); - int damage = item.damage().orElse(0) + amount; - if (damage > item.maxDamage().orElse((int) itemStack.getType().getMaxDurability())) { - itemStack.setAmount(0); - } else { - item.damage(damage); - itemStack.setItemMeta(item.load().getItemMeta()); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java deleted file mode 100644 index 630876cb0..000000000 --- a/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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.util; - -import net.momirealms.customcrops.api.mechanic.misc.CRotation; -import org.bukkit.Rotation; - -import java.util.Random; - -public class RotationUtils { - - private static final Rotation[] rotationsI = {Rotation.NONE, Rotation.FLIPPED, Rotation.CLOCKWISE, Rotation.COUNTER_CLOCKWISE}; - private static final float[] rotationsF = {0f, 90f, 180f, -90f}; - - public static Rotation getRandomBukkitRotation() { - return rotationsI[new Random().nextInt(4)]; - } - - public static float getRandomFloatRotation() { - return rotationsF[new Random().nextInt(4)]; - } - - public static float getFloatRotation(CRotation cRotation) { - if (cRotation == CRotation.RANDOM) { - return getRandomFloatRotation(); - } - return cRotation.getYaw(); - } - - public static Rotation getBukkitRotation(CRotation cRotation) { - switch (cRotation) { - case RANDOM -> { - return getRandomBukkitRotation(); - } - case EAST -> { - return Rotation.COUNTER_CLOCKWISE; - } - case WEST -> { - return Rotation.CLOCKWISE; - } - case NORTH -> { - return Rotation.FLIPPED; - } - default -> { - return Rotation.NONE; - } - } - } -} diff --git a/plugin/src/main/resources/commands.yml b/plugin/src/main/resources/commands.yml new file mode 100644 index 000000000..4c1eba9a8 --- /dev/null +++ b/plugin/src/main/resources/commands.yml @@ -0,0 +1,38 @@ +# +# Don't change this +# +config-version: "${config_version}" + +# +# For safety reasons, editing this file requires a restart to apply +# + +# A command to reload the plugin +# Usage: [COMMAND] +reload: + enable: true + permission: customcrops.command.reload + usage: + - /customcrops reload + - /ccrops reload + +get_season: + enable: true + permission: customcrops.command.get_season + usage: + - /customcrops season get + - /ccrops season get + +set_season: + enable: true + permission: customcrops.command.set_season + usage: + - /customcrops season set + - /ccrops season set + +debug_data: + enable: true + permission: customcrops.command.debug + usage: + - /customcrops debug data + - /ccrops debug data diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 9460b5e34..cc585c971 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,30 +1,26 @@ # Don't change config-version: '37' - # Debug debug: false - # BStats metrics: true - # Check updates update-checker: true - -# Language -# https://github.com/Xiao-MoMi/Custom-Crops/tree/main/plugin/src/main/resources/messages -lang: en - +# Force locale, for instance zh_cn +force-locale: '' # World settings worlds: - # This is designed for servers that using an independent folder for worlds - # Especially for realm systems + # Some servers use separate folders to store player worlds, and these world directories + # are often not located in the server's root directory. This option allows users to read + # world data from a custom directory. It is only applicable to Bukkit worlds. absolute-world-folder-path: '' # A list of worlds that would decide where the plugin mechanisms take effect # Mode: whitelist/blacklist/regex mode: blacklist list: - - blacklist_world + - blacklist_world # A non-existent world settings: + # Default settings that apply to all worlds _DEFAULT_: # Whether to enable the plugin's pre-made system # Disabling this option will make all mechanisms stop counting ticks unless you have full control over it using the API. @@ -37,10 +33,9 @@ worlds: auto-alternation: true # Game days of each season duration: 28 - # Random tick speed # It's different from the vanilla RandomTickSpeed. - # CustomCrops' random tick speed has a larger base value - # So we can have more precise control on crops' growth speed + # "Random tick" here refers to the process of randomly selecting n blocks within a 16x16x16 section every second to perform a tick while Minecraft do that every tick. + # Therefore, the random tick of CustomCrops has little impact on the server, and it is performed on multiple threads. random-tick-speed: 20 # The smallest tick unit in seconds used in scheduled tick mode # 300s means that a crop would be certainly ticked once in 300s @@ -66,6 +61,7 @@ worlds: # Scheduler mode provides more reliable growth schedule management # which allows crops to grow at almost the same speed mode: RANDOM_TICK + # The tick-interval determines how many times a block is ticked before its tick logic is actually executed. tick-interval: 1 # Limit the max amount of crops in one chunk (-1 = no limit) max-per-chunk: -1 @@ -74,14 +70,12 @@ worlds: # RANDOM_TICK / SCHEDULED_TICK mode: SCHEDULED_TICK tick-interval: 3 - # Limit the max amount of pots in one chunk (-1 = no limit) max-per-chunk: -1 # Settings for sprinklers sprinkler: # RANDOM_TICK / SCHEDULED_TICK mode: SCHEDULED_TICK tick-interval: 2 - # Limit the max amount of sprinklers in one chunk (-1 = no limit) max-per-chunk: -1 # You can override the default settings for worlds here _WORLDS_: @@ -89,7 +83,6 @@ worlds: enable: false world_the_end: enable: false - # Mechanics settings mechanics: # You can create more ranks by adding more "/" for instance x/x/x/x/x @@ -97,29 +90,26 @@ mechanics: # 17/2/1 = 85%/10%/5% # 85% = 17/(17+2+1) * 100% default-quality-ratio: 17/2/1 - # Scarecrow prevents crops from being attacked by crows scarecrow: enable: true id: '{0}scarecrow' type: ITEM_FRAME range: 7 - # If this option is enabled, the range above would not longer take effect - # This option would make the scarecrow protect all the crops in a chunk instead of crops in certain range + # If this option is enabled, the range would no longer take effect + # This option would make it protect all the crops in the same chunk protect-chunk: false - # Greenhouse glass prevents crops from withering from season changing greenhouse: enable: true + # You can use a list of ids here, vanilla blocks are supported too id: '{0}greenhouse_glass' type: CHORUS range: 5 - # Sync seasons sync-season: enable: false reference: world - # Vanilla farmland settings vanilla-farmland: # Disable vanilla farmland moisture mechanics @@ -127,29 +117,19 @@ mechanics: disable-moisture-mechanic: false # Prevent entities from trampling the farmland prevent-trampling: false - other-settings: # It's recommended to use MiniMessage format. If you insist on using legacy color code "&", enable the support below. # Disable this would improve performance legacy-color-code-support: true - # Requires PlaceholderAPI to work placeholder-register: '{skill-level}': '%levelplugin_farming%' - - # Thread pool settings - thread-pool-settings: - corePoolSize: 10 - maximumPoolSize: 10 - keepAliveTime: 30 - # Using items from other plugins item-detection-order: [] - # Whether to protect the original lore of the item # This uses the scoreboard component to identify the plugin's lore, # which may conflict with some plugins that still use SpigotAPI#ItemMeta. protect-original-lore: false - - # Should the plugin try to convert worlds from 3.3 to 3.4 when WorldLoadEvent is triggered - convert-on-world-load: false \ No newline at end of file + # Whether to check if the block/furniture corresponds with the one in CustomCrops data + # You can enable this if you are using Oraxen as its API is more reliable + double-check: false \ No newline at end of file diff --git a/plugin/src/main/resources/contents/crops/default.yml b/plugin/src/main/resources/contents/crops/default.yml index 1bc252be1..b8ff75259 100644 --- a/plugin/src/main/resources/contents/crops/default.yml +++ b/plugin/src/main/resources/contents/crops/default.yml @@ -215,14 +215,17 @@ tomato: crop: tomato # Custom grow conditions grow-conditions: - season_condition: - type: suitable_season - value: - - Spring - - Autumn - water_condition: - type: water_more_than - value: 0 + default: + point: 1 + conditions: + season_condition: + type: suitable_season + value: + - Spring + - Autumn + water_condition: + type: water_more_than + value: 0 # Custom death conditions death-conditions: no_water: diff --git a/plugin/src/main/resources/contents/pots/default.yml b/plugin/src/main/resources/contents/pots/default.yml index aef4cdf09..8b95198d5 100644 --- a/plugin/src/main/resources/contents/pots/default.yml +++ b/plugin/src/main/resources/contents/pots/default.yml @@ -13,20 +13,19 @@ default: absorb-nearby-water: false # Set unique looks for pots with different fertilizer statuses fertilized-pots: - enable: false quality: dry: {0}dry_pot wet: {0}wet_pot - yield-increase: + yield_increase: dry: {0}dry_pot wet: {0}wet_pot variation: dry: {0}dry_pot wet: {0}wet_pot - soil-retain: + soil_retain: dry: {0}dry_pot wet: {0}wet_pot - speed-grow: + speed_grow: dry: {0}dry_pot wet: {0}wet_pot # Methods to fill the watering can diff --git a/plugin/src/main/resources/messages/en.yml b/plugin/src/main/resources/messages/en.yml deleted file mode 100644 index 6f4ce579a..000000000 --- a/plugin/src/main/resources/messages/en.yml +++ /dev/null @@ -1,8 +0,0 @@ -messages: - prefix: '[CustomCrops] ' - reload: 'Reloaded! Took {time}ms.' - spring: 'Spring' - summer: 'Summer' - autumn: 'Autumn' - winter: 'Winter' - no-season: 'Season Disabled' \ No newline at end of file diff --git a/plugin/src/main/resources/messages/es.yml b/plugin/src/main/resources/messages/es.yml deleted file mode 100644 index a638c3461..000000000 --- a/plugin/src/main/resources/messages/es.yml +++ /dev/null @@ -1,8 +0,0 @@ -messages: - prefix: '[CustomCrops] ' - reload: '¡Recargado! Tardó {time}ms.' - spring: 'Primavera' - summer: 'Verano' - autumn: 'Otoño' - winter: 'Invierno' - no-season: 'LAS ESTACIONES ESTÁN DESACTIVADAS EN ESTE MUNDO' \ No newline at end of file diff --git a/plugin/src/main/resources/messages/fr.yml b/plugin/src/main/resources/messages/fr.yml deleted file mode 100644 index e7dc40c12..000000000 --- a/plugin/src/main/resources/messages/fr.yml +++ /dev/null @@ -1,8 +0,0 @@ -messages: - prefix: '[CustomCrops] ' - reload: 'Rechargé ! A pris {time}ms.' - spring: 'Printemps' - summer: 'Été' - autumn: 'Automne' - winter: 'Hiver' - no-season: 'SAISON DÉSACTIVÉE DANS CE MONDE' \ No newline at end of file diff --git a/plugin/src/main/resources/messages/pt.yml b/plugin/src/main/resources/messages/pt.yml deleted file mode 100644 index 7353c8b2e..000000000 --- a/plugin/src/main/resources/messages/pt.yml +++ /dev/null @@ -1,8 +0,0 @@ -messages: - prefix: '[CustomCrops] ' - reload: 'Recarregado! Levou {time}ms.' - spring: 'Primavera' - summer: 'Verão' - autumn: 'Outono' - winter: 'Inverno' - no-season: 'Estação Desativada' diff --git a/plugin/src/main/resources/messages/ru.yml b/plugin/src/main/resources/messages/ru.yml deleted file mode 100644 index c15e60cac..000000000 --- a/plugin/src/main/resources/messages/ru.yml +++ /dev/null @@ -1,8 +0,0 @@ -messages: - prefix: '[CustomCrops] ' - reload: 'Перезагружено! Заняло {time}мс.' - spring: 'Весна' - summer: 'Лето' - autumn: 'Осень' - winter: 'Зима' - no-season: 'СЕЗОНЫ ОТКЛЮЧЕНЫ В ЭТОМ МИРЕ' \ No newline at end of file diff --git a/plugin/src/main/resources/messages/zh_cn.yml b/plugin/src/main/resources/messages/zh_cn.yml deleted file mode 100644 index 3fef4d45e..000000000 --- a/plugin/src/main/resources/messages/zh_cn.yml +++ /dev/null @@ -1,8 +0,0 @@ -messages: - prefix: '[CustomCrops] ' - reload: '重载完成! 耗时 {time}ms.' - spring: '春季' - summer: '夏季' - autumn: '秋季' - winter: '冬季' - no-season: '未启用季节' \ No newline at end of file diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index d16b5caeb..7ff6f5e36 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -1,12 +1,10 @@ name: CustomCrops -version: '${version}' -main: net.momirealms.customcrops.CustomCropsPluginImpl +version: '${project_version}' +main: net.momirealms.customcrops.bukkit.BukkitBootstrap api-version: 1.17 load: POSTWORLD authors: [ XiaoMoMi ] folia-supported: true -depend: - - ProtocolLib softdepend: - Vault - ItemsAdder @@ -18,7 +16,7 @@ softdepend: - RealisticSeasons - AdvancedSeasons - SlimeWorldManager - - SlimeWorldPlugin + - MythicMobs - HuskClaims - HuskTowns - Residence @@ -29,7 +27,6 @@ softdepend: - GriefPrevention - BentoBox - IridiumSkyblock - - MythicMobs - KingdomsX - Landlord - Lands diff --git a/plugin/src/main/resources/translations/en.yml b/plugin/src/main/resources/translations/en.yml new file mode 100644 index 000000000..5d09ab179 --- /dev/null +++ b/plugin/src/main/resources/translations/en.yml @@ -0,0 +1,46 @@ +# Don"t change this +config-version: "37" + +exception.invalid_syntax: "Invalid syntax. Correct syntax: " +exception.invalid_argument: "Invalid argument. Reason: " +exception.invalid_sender: " is not allowed to execute that command. Must be of type " +exception.unexpected: "An internal error occurred while attempting to perform this command" +exception.no_permission: "I'm sorry, but you do not have permission to perform this command" +exception.no_such_command: "Unknown command." +argument.entity.notfound.player: "" +argument.entity.notfound.entity: "" +argument.parse.failure.time: "'' is not a valid time format" +argument.parse.failure.material: "'' is not a valid material name" +argument.parse.failure.enchantment: "'' is not a valid enchantment" +argument.parse.failure.offlineplayer: "No player found for input ''" +argument.parse.failure.player: "No player found for input ''" +argument.parse.failure.world: "'' is not a valid Minecraft world" +argument.parse.failure.location.invalid_format: "'' is not a valid location. Required format is ' " +argument.parse.failure.location.mixed_local_absolute: "Cannot mix local and absolute coordinates. (either all coordinates use '^' or none do)" +argument.parse.failure.namespacedkey.namespace: "Invalid namespace ''. Must be [a-z0-9._-]" +argument.parse.failure.namespacedkey.key: "Invalid key ''. Must be [a-z0-9/._-]" +argument.parse.failure.namespacedkey.need_namespace: "Invalid input '', requires an explicit namespace" +argument.parse.failure.boolean: "Could not parse boolean from ''" +argument.parse.failure.number: "'' is not a valid number in the range to " +argument.parse.failure.char: "'' is not a valid character" +argument.parse.failure.string: "'' is not a valid string of type " +argument.parse.failure.uuid: "'' is not a valid UUID" +argument.parse.failure.enum: "'' is not one of the following: " +argument.parse.failure.regex: "'' does not match ''" +argument.parse.failure.flag.unknown: "Unknown flag ''" +argument.parse.failure.flag.duplicate_flag: "Duplicate flag ''" +argument.parse.failure.flag.no_flag_started: "No flag started. Don't know what to do with ''" +argument.parse.failure.flag.missing_argument: "Missing argument for ''" +argument.parse.failure.flag.no_permission: "You don't have permission to use ''" +argument.parse.failure.color: "'' is not a valid color" +argument.parse.failure.duration: "'' is not a duration format" +argument.parse.failure.aggregate.missing: "Missing component ''" +argument.parse.failure.aggregate.failure: "Invalid component '': " +argument.parse.failure.either: "Could not resolve or from ''" +argument.parse.failure.namedtextcolor: "'' is not a named text color" +command.reload.success: "Reloaded. Took ms." +season.spring: "Spring" +season.summer: "Summer" +season.autumn: "Autumn" +season.winter: "Winter" +season.disable: "Disable" \ No newline at end of file diff --git a/plugin/src/main/resources/translations/zh_cn.yml b/plugin/src/main/resources/translations/zh_cn.yml new file mode 100644 index 000000000..acb944613 --- /dev/null +++ b/plugin/src/main/resources/translations/zh_cn.yml @@ -0,0 +1,46 @@ +# 别动这个 +config-version: "37" + +exception.invalid_syntax: "无效语法. 正确语法:" +exception.invalid_argument: "无效参数. 原因:" +exception.invalid_sender: " 不允许执行该命令. 执行者必须是 " +exception.unexpected: "执行该命令时发生内部错误" +exception.no_permission: "抱歉, 您没有权限执行该命令" +exception.no_such_command: "未知命令" +argument.entity.notfound.player: "找不到玩家 ''" +argument.entity.notfound.entity: "找不到实体 ''" +argument.parse.failure.time: "'' 不是有效的时间格式" +argument.parse.failure.material: "'' 不是有效的材料" +argument.parse.failure.enchantment: "'' 不是有效的魔咒" +argument.parse.failure.offlineplayer: "输入的玩家 '' 已离线" +argument.parse.failure.player: "找不到输入的玩家 ''" +argument.parse.failure.world: "'' 不是有效的 Minecraft 世界名称" +argument.parse.failure.location.invalid_format: "'' 不是有效的位置格式.必须格式为 ' '" +argument.parse.failure.location.mixed_local_absolute: "不能混用相对和绝对坐标.坐标要么全部使用 '^',要么全部不用" +argument.parse.failure.namespacedkey.namespace: "无效的命名空间 ''.必须为 [a-z0-9._-]" +argument.parse.failure.namespacedkey.key: "无效的键 ''.必须为 [a-z0-9/._-]" +argument.parse.failure.namespacedkey.need_namespace: "无效的输入 '', 需要显式指定命名空间" +argument.parse.failure.boolean: "无法解析布尔值 ''" +argument.parse.failure.number: "'' 不是从 范围内的有效数字" +argument.parse.failure.char: "'' 不是有效的字符" +argument.parse.failure.string: "'' 不是类型为 的有效字符串" +argument.parse.failure.uuid: "'' 不是有效的 UUID" +argument.parse.failure.enum: "'' 不是以下任何一种情况之一: " +argument.parse.failure.regex: "'' 不匹配 ''" +argument.parse.failure.flag.unknown: "未知标志 ''" +argument.parse.failure.flag.duplicate_flag: "重复的标志 ''" +argument.parse.failure.flag.no_flag_started: "没有开始标志. 不知道如何处理 ''" +argument.parse.failure.flag.missing_argument: "缺少 '' 参数" +argument.parse.failure.flag.no_permission: "您没有权限使用 ''" +argument.parse.failure.color: "'' 不是有效的颜色" +argument.parse.failure.duration: "'' 不是有效的持续时间格式" +argument.parse.failure.aggregate.missing: "缺少组件 ''" +argument.parse.failure.aggregate.failure: "无效的组件 '': " +argument.parse.failure.either: "无法从 '' 解析 " +argument.parse.failure.namedtextcolor: "'' 不是颜色代码" +command.reload.success: "重新加载完成.耗时 毫秒译者:jhqwqmc" +season.spring: "春" +season.summer: "夏" +season.autumn: "秋" +season.winter: "冬" +season.disable: "未启用" \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..760b8fd20 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,12 @@ +rootProject.name = "CustomCrops" +include(":common") +include(":api") +include(":plugin") +include(":compatibility") +include(":compatibility-asp-r1") +include(":compatibility-oraxen-r1") +include(":compatibility-oraxen-r2") +include(":compatibility-itemsadder-r1") + + + From 694a51e512331ef7e920e4f099a13b18682323b8 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sat, 31 Aug 2024 23:03:09 +0800 Subject: [PATCH 065/329] Delete settings.gradle --- settings.gradle | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 settings.gradle diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 5a9da6429..000000000 --- a/settings.gradle +++ /dev/null @@ -1,7 +0,0 @@ -rootProject.name = 'CustomCrops' -include(":plugin") -include(":api") -include(":legacy-api") -include("oraxen-legacy") -include(":oraxen-j21") - From f632b99554050273f28ec8a3463394a9db728014 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sat, 31 Aug 2024 23:24:02 +0800 Subject: [PATCH 066/329] 3.6 --- .../api/action/AbstractActionManager.java | 25 ++++++++++--- .../api/context/AbstractContext.java | 17 +++++++++ .../api/context/BlockContextImpl.java | 17 +++++++++ .../api/core/AbstractCustomEventListener.java | 17 +++++++++ .../api/core/AbstractItemManager.java | 17 +++++++++ .../api/core/BuiltInBlockMechanics.java | 17 +++++++++ .../api/core/BuiltInItemMechanics.java | 17 +++++++++ .../api/core/ClearableMappedRegistry.java | 17 +++++++++ .../api/core/ClearableRegistry.java | 17 +++++++++ .../customcrops/api/core/ConfigManager.java | 4 +-- .../customcrops/api/core/CustomForm.java | 17 +++++++++ .../api/core/CustomItemProvider.java | 17 +++++++++ .../customcrops/api/core/ExistenceForm.java | 17 +++++++++ .../api/core/FurnitureRotation.java | 17 +++++++++ .../customcrops/api/core/IdMap.java | 17 +++++++++ .../api/core/InteractionResult.java | 17 +++++++++ .../customcrops/api/core/ItemManager.java | 17 +++++++++ .../customcrops/api/core/MappedRegistry.java | 17 +++++++++ .../customcrops/api/core/Registries.java | 17 +++++++++ .../customcrops/api/core/Registry.java | 17 +++++++++ .../customcrops/api/core/RegistryAccess.java | 17 +++++++++ .../api/core/SimpleRegistryAccess.java | 17 +++++++++ .../customcrops/api/core/StatedItem.java | 17 +++++++++ .../api/core/WriteableRegistry.java | 17 +++++++++ .../core/block/AbstractCustomCropsBlock.java | 17 +++++++++ .../api/core/block/BreakReason.java | 21 ++++++++--- .../customcrops/api/core/block/CropBlock.java | 17 +++++++++ .../api/core/block/CropConfig.java | 17 +++++++++ .../api/core/block/CropConfigImpl.java | 17 +++++++++ .../api/core/block/CropStageConfig.java | 17 +++++++++ .../api/core/block/CropStageConfigImpl.java | 17 +++++++++ .../api/core/block/CustomCropsBlock.java | 17 +++++++++ .../api/core/block/DeathCondition.java | 17 +++++++++ .../api/core/block/GreenhouseBlock.java | 17 +++++++++ .../api/core/block/GrowCondition.java | 17 +++++++++ .../customcrops/api/core/block/PotBlock.java | 19 +++++++++- .../customcrops/api/core/block/PotConfig.java | 21 +++++++++-- .../api/core/block/PotConfigImpl.java | 21 +++++++++-- .../api/core/block/ScarecrowBlock.java | 17 +++++++++ .../api/core/block/SprinklerBlock.java | 19 +++++++++- .../api/core/block/SprinklerConfig.java | 21 +++++++++-- .../api/core/block/SprinklerConfigImpl.java | 21 +++++++++-- .../core/item/AbstractCustomCropsItem.java | 17 +++++++++ .../core/item/AbstractFertilizerConfig.java | 17 +++++++++ .../api/core/item/CustomCropsItem.java | 17 +++++++++ .../customcrops/api/core/item/Fertilizer.java | 17 +++++++++ .../api/core/item/FertilizerConfig.java | 17 +++++++++ .../api/core/item/FertilizerImpl.java | 17 +++++++++ .../api/core/item/FertilizerItem.java | 17 +++++++++ .../customcrops/api/core/item/Quality.java | 17 +++++++++ .../api/core/item/QualityImpl.java | 17 +++++++++ .../customcrops/api/core/item/SeedItem.java | 17 +++++++++ .../customcrops/api/core/item/SoilRetain.java | 17 +++++++++ .../api/core/item/SoilRetainImpl.java | 17 +++++++++ .../customcrops/api/core/item/SpeedGrow.java | 17 +++++++++ .../api/core/item/SpeedGrowImpl.java | 17 +++++++++ .../api/core/item/SprinklerItem.java | 17 +++++++++ .../customcrops/api/core/item/Variation.java | 17 +++++++++ .../api/core/item/VariationImpl.java | 17 +++++++++ .../api/core/item/WateringCanConfig.java | 21 +++++++++-- .../api/core/item/WateringCanConfigImpl.java | 21 +++++++++-- .../api/core/item/WateringCanItem.java | 19 +++++++++- .../api/core/item/YieldIncrease.java | 17 +++++++++ .../api/core/item/YieldIncreaseImpl.java | 17 +++++++++ .../api/core/world/CustomCropsBlockState.java | 17 +++++++++ .../core/world/CustomCropsBlockStateImpl.java | 17 +++++++++ .../api/core/world/CustomCropsChunkImpl.java | 17 +++++++++ .../api/core/world/CustomCropsRegionImpl.java | 17 +++++++++ .../core/world/CustomCropsSectionImpl.java | 17 +++++++++ .../api/core/world/CustomCropsWorldImpl.java | 17 +++++++++ .../api/core/world/WorldManager.java | 17 +++++++++ .../world/adaptor/AbstractWorldAdaptor.java | 17 +++++++++ .../api/core/world/adaptor/WorldAdaptor.java | 17 +++++++++ .../api/core/wrapper/WrappedBreakEvent.java | 17 +++++++++ .../core/wrapper/WrappedInteractAirEvent.java | 17 +++++++++ .../core/wrapper/WrappedInteractEvent.java | 17 +++++++++ .../api/core/wrapper/WrappedPlaceEvent.java | 17 +++++++++ .../customcrops/api/event/PotFillEvent.java | 2 +- .../api/event/SprinklerFillEvent.java | 2 +- .../api/event/WateringCanFillEvent.java | 2 +- .../{core => misc}/water/AbstractMethod.java | 2 +- .../api/{core => misc}/water/FillMethod.java | 2 +- .../api/misc/{ => water}/WaterBar.java | 2 +- .../{core => misc}/water/WateringMethod.java | 2 +- .../AbstractRequirementManager.java | 17 +++++++++ .../api/util/DummyCancellable.java | 35 +++++++++++++++++++ .../customcrops/api/util/FakeCancellable.java | 18 ---------- .../customcrops/api/util/PluginUtils.java | 17 +++++++++ .../customcrops/api/util/StringUtils.java | 17 +++++++++ .../common/annotation/DoNotUse.java | 17 +++++++++ .../customcrops/common/util/Key.java | 17 +++++++++ .../adaptor/asp_r1/SlimeWorldAdaptorR1.java | 17 +++++++++ .../itemsadder_r1/ItemsAdderListener.java | 21 +++++++++-- .../itemsadder_r1/ItemsAdderProvider.java | 17 +++++++++ .../custom/oraxen_r1/OraxenListener.java | 17 +++++++++ .../custom/oraxen_r1/OraxenProvider.java | 17 +++++++++ .../custom/oraxen_r2/OraxenListener.java | 17 +++++++++ .../custom/oraxen_r2/OraxenProvider.java | 17 +++++++++ .../integration/papi/CustomCropsPapi.java | 17 +++++++++ .../bukkit/BukkitCustomCropsPluginImpl.java | 17 +++++++++ .../bukkit/action/BlockActionManager.java | 17 +++++++++ .../bukkit/action/PlayerActionManager.java | 17 +++++++++ .../bukkit/config/BukkitConfigManager.java | 17 +++++++++ .../customcrops/bukkit/config/ConfigType.java | 2 +- .../adaptor/BukkitWorldAdaptor.java | 17 +++++++++ .../bukkit/item/BukkitItemFactory.java | 17 +++++++++ .../bukkit/item/BukkitItemManager.java | 17 +++++++++ .../bukkit/item/ComponentItemFactory.java | 17 +++++++++ .../bukkit/item/UniversalItemFactory.java | 17 +++++++++ .../requirement/BlockRequirementManager.java | 17 +++++++++ .../requirement/PlayerRequirementManager.java | 17 +++++++++ .../bukkit/scheduler/DummyTask.java | 17 +++++++++ .../bukkit/world/BukkitWorldManager.java | 17 +++++++++ 113 files changed, 1800 insertions(+), 53 deletions(-) rename api/src/main/java/net/momirealms/customcrops/api/{core => misc}/water/AbstractMethod.java (97%) rename api/src/main/java/net/momirealms/customcrops/api/{core => misc}/water/FillMethod.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/misc/{ => water}/WaterBar.java (95%) rename api/src/main/java/net/momirealms/customcrops/api/{core => misc}/water/WateringMethod.java (97%) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/DummyCancellable.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/util/FakeCancellable.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java index 5edb7381b..b4f5fd59a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java @@ -1,3 +1,20 @@ +/* + * 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.action; import dev.dejvokep.boostedyaml.block.implementation.Section; @@ -871,15 +888,15 @@ protected void registerBreakAction() { if (context.holder() instanceof Player p) { player = p; } - FakeCancellable fakeCancellable = new FakeCancellable(); + DummyCancellable dummyCancellable = new DummyCancellable(); if (player != null) { EquipmentSlot slot = requireNonNull(context.arg(ContextKeys.SLOT)); ItemStack itemStack = player.getInventory().getItem(slot); - state.type().onBreak(new WrappedBreakEvent(player, null, world, location, stageConfig.stageID(), itemStack, plugin.getItemManager().id(itemStack), BreakReason.ACTION, fakeCancellable)); + state.type().onBreak(new WrappedBreakEvent(player, null, world, location, stageConfig.stageID(), itemStack, plugin.getItemManager().id(itemStack), BreakReason.ACTION, dummyCancellable)); } else { - state.type().onBreak(new WrappedBreakEvent(null, null, world, location, stageConfig.stageID(), null, null, BreakReason.ACTION, fakeCancellable)); + state.type().onBreak(new WrappedBreakEvent(null, null, world, location, stageConfig.stageID(), null, null, BreakReason.ACTION, dummyCancellable)); } - if (fakeCancellable.isCancelled()) { + if (dummyCancellable.isCancelled()) { return; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/AbstractContext.java b/api/src/main/java/net/momirealms/customcrops/api/context/AbstractContext.java index effa86cca..111ddf9c1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/AbstractContext.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/AbstractContext.java @@ -1,3 +1,20 @@ +/* + * 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.context; import org.jetbrains.annotations.Nullable; diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java b/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java index c39cf7e56..5d28e1868 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java @@ -1,3 +1,20 @@ +/* + * 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.context; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java index 098b70cde..172f78367 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.helper.VersionHelper; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java index 7000beff8..b8f3a23c5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java @@ -1,3 +1,20 @@ +/* + * 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.core; import org.bukkit.Location; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java index 2a7a6ce74..986697d58 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java @@ -1,3 +1,20 @@ +/* + * 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.core; import com.flowpowered.nbt.CompoundMap; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java index 5e8dad986..97624fe15 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java index 5fdb9bc0c..81ea6f6a8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java index e1c539e8a..219b822e7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java @@ -1,3 +1,20 @@ +/* + * 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.core; public interface ClearableRegistry extends WriteableRegistry { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java index 4b99df846..915614c46 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -16,8 +16,8 @@ import net.momirealms.customcrops.api.core.item.FertilizerConfig; import net.momirealms.customcrops.api.core.item.FertilizerType; import net.momirealms.customcrops.api.core.item.WateringCanConfig; -import net.momirealms.customcrops.api.core.water.FillMethod; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.FillMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.util.PluginUtils; import net.momirealms.customcrops.common.config.ConfigLoader; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/CustomForm.java b/api/src/main/java/net/momirealms/customcrops/api/core/CustomForm.java index c3d5e01d4..622af3d62 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/CustomForm.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/CustomForm.java @@ -1,3 +1,20 @@ +/* + * 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.core; public enum CustomForm { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java b/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java index eaf913bbf..2d62a3f6b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java @@ -1,3 +1,20 @@ +/* + * 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.core; import org.bukkit.Location; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ExistenceForm.java b/api/src/main/java/net/momirealms/customcrops/api/core/ExistenceForm.java index 1a3a53d40..113daeeed 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ExistenceForm.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ExistenceForm.java @@ -1,3 +1,20 @@ +/* + * 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.core; public enum ExistenceForm { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java b/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java index 481776e03..57409f992 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.util.RandomUtils; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java b/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java index 4dded1c94..c9e7b5471 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java @@ -1,3 +1,20 @@ +/* + * 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.core; import javax.annotation.Nullable; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java b/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java index f2af6f7d0..8c5c86b5d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java @@ -1,3 +1,20 @@ +/* + * 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.core; public enum InteractionResult { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java index bedbaca74..e667237c8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.item.Item; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java index 0d576e028..53994b4d0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java index e84e4de3e..d18943297 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.api.core.block.CropConfig; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java index d092e7107..f4ee23fa9 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java index 923cae2fe..c5cfa9e9b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java index 861227c04..3a80d8d01 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java index f04bd0d1d..4dc3573fb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java @@ -1,3 +1,20 @@ +/* + * 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.core; import net.momirealms.customcrops.api.context.Context; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java index a839a3272..d5caf8715 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java @@ -1,3 +1,20 @@ +/* + * 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.core; public interface WriteableRegistry extends Registry { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java index 8424c8c99..21813ff46 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import com.flowpowered.nbt.CompoundMap; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/BreakReason.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/BreakReason.java index f2b85a756..e38177781 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/BreakReason.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/BreakReason.java @@ -1,12 +1,25 @@ +/* + * 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.core.block; public enum BreakReason { - // Crop was broken by a player BREAK, - // Crop was trampled TRAMPLE, - // Crop was broken due to an explosion (block or entity) EXPLODE, - // Crop was broken due to a specific action ACTION } 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 514336805..616d490a5 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 @@ -1,3 +1,20 @@ +/* + * 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.core.block; import com.flowpowered.nbt.IntTag; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java index a917314d5..08ee3aa22 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java index bc0554a19..e0862ecb6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import com.google.common.base.Preconditions; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java index beb9448f5..0ec236cfe 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java index 46502899c..8a2f1d713 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java index 9515fc30b..c40d08826 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import com.flowpowered.nbt.CompoundMap; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java index e15a32d10..cfedff1d2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.context.Context; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java index b0dbc8ebb..d74227688 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java index b8f856150..c03b598b8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.context.Context; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 008069140..4a125941b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import com.flowpowered.nbt.*; @@ -7,7 +24,7 @@ import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.item.Fertilizer; import net.momirealms.customcrops.api.core.item.FertilizerConfig; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java index 7ec309e29..5de6fdddc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java @@ -1,10 +1,27 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.item.FertilizerType; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; -import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.requirement.Requirement; import net.momirealms.customcrops.common.util.Pair; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java index 18b115e45..05cb6bae6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java @@ -1,11 +1,28 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; import net.momirealms.customcrops.api.core.item.FertilizerType; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; -import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.requirement.Requirement; import net.momirealms.customcrops.common.util.Pair; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java index 18808248b..5ac78b97d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java index 3b6373a82..850b816ee 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -1,3 +1,20 @@ +/* + * 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.core.block; import com.flowpowered.nbt.IntTag; @@ -5,7 +22,7 @@ import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java index c9a85b996..9b6c95e32 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java @@ -1,10 +1,27 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; -import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.requirement.Requirement; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java index 3a3a52a18..58472ec36 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java @@ -1,10 +1,27 @@ +/* + * 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.core.block; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; -import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.requirement.Requirement; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java index b6c307fa5..5913a3ddd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java index f0f608862..1f930b9e4 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java index 6358f3f31..2b3198c85 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java index b685e7ab5..057729249 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.core.Registries; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java index e556e5713..6919c43e4 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java index 562d7f8f9..53013acab 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; public class FertilizerImpl implements Fertilizer { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java index 47724e996..82bdfcb8c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java index e7ab798c0..80db0ff95 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java index 6370c93f0..3895ed887 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java index ea7fc87d0..9cd844ae0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java index 70f39e51b..36d045f3c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java index 965d58c79..8a923b894 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java index 141cb6b2b..416898e2c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java index 0734d2f4e..48f1c2ad8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java index eebd8dec4..93cf1ca26 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java index 289b86b18..c57482eb0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java index 45b438408..5d2b850c6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java index 68ddbef36..ce67f54a4 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java @@ -1,8 +1,25 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; -import net.momirealms.customcrops.api.core.water.FillMethod; -import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.water.FillMethod; +import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.requirement.Requirement; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java index 1906816be..ca1d61cf3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java @@ -1,8 +1,25 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; -import net.momirealms.customcrops.api.core.water.FillMethod; -import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.water.FillMethod; +import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.requirement.Requirement; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java index 96bb2ef57..2d5218c6a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.kyori.adventure.text.Component; @@ -8,7 +25,7 @@ import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.block.*; -import net.momirealms.customcrops.api.core.water.FillMethod; +import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java index ed352b5ee..f455f503b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java index d7da16b4d..867946715 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.item; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java index 6b1dec8f0..acd2c11aa 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java @@ -1,3 +1,20 @@ +/* + * 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.core.world; import com.flowpowered.nbt.CompoundMap; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockStateImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockStateImpl.java index 5dd6a2d9b..2f41cd8ec 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockStateImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockStateImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.world; import com.flowpowered.nbt.CompoundMap; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java index 77fc3aa12..ba3269aae 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.world; import net.momirealms.customcrops.common.util.RandomUtils; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java index a6ebed2e9..04afd671b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.world; import org.jetbrains.annotations.NotNull; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSectionImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSectionImpl.java index 891afa0d5..af71593f6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSectionImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSectionImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.world; import org.jetbrains.annotations.NotNull; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java index 1c8744cac..cd92342d9 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java @@ -1,3 +1,20 @@ +/* + * 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.core.world; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java index 3905e5e93..6dcaef677 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java @@ -1,3 +1,20 @@ +/* + * 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.core.world; import net.momirealms.customcrops.api.core.world.adaptor.WorldAdaptor; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/AbstractWorldAdaptor.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/AbstractWorldAdaptor.java index 4934a6f10..e14d2503b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/AbstractWorldAdaptor.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/AbstractWorldAdaptor.java @@ -1,3 +1,20 @@ +/* + * 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.core.world.adaptor; import com.flowpowered.nbt.CompoundMap; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java index 8e578c54c..82272b03d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java @@ -1,3 +1,20 @@ +/* + * 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.core.world.adaptor; import net.momirealms.customcrops.api.core.world.*; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedBreakEvent.java index 88709352f..5d28aacae 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedBreakEvent.java @@ -1,3 +1,20 @@ +/* + * 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.core.wrapper; import net.momirealms.customcrops.api.core.block.BreakReason; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractAirEvent.java b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractAirEvent.java index c827c2f55..c6a298721 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractAirEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractAirEvent.java @@ -1,3 +1,20 @@ +/* + * 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.core.wrapper; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractEvent.java index c114d3d32..0647eef93 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedInteractEvent.java @@ -1,3 +1,20 @@ +/* + * 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.core.wrapper; import net.momirealms.customcrops.api.core.ExistenceForm; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedPlaceEvent.java index 612e799c8..088026769 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/wrapper/WrappedPlaceEvent.java @@ -1,3 +1,20 @@ +/* + * 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.core.wrapper; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java index 2b7323353..3d5141cd7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java @@ -18,7 +18,7 @@ package net.momirealms.customcrops.api.event; import net.momirealms.customcrops.api.core.block.PotConfig; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java index ae59f85e2..ace165e2d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java @@ -18,7 +18,7 @@ package net.momirealms.customcrops.api.event; import net.momirealms.customcrops.api.core.block.SprinklerConfig; -import net.momirealms.customcrops.api.core.water.WateringMethod; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java index 05b930a7e..6dce329a5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java @@ -18,7 +18,7 @@ package net.momirealms.customcrops.api.event; import net.momirealms.customcrops.api.core.item.WateringCanConfig; -import net.momirealms.customcrops.api.core.water.FillMethod; +import net.momirealms.customcrops.api.misc.water.FillMethod; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/water/AbstractMethod.java b/api/src/main/java/net/momirealms/customcrops/api/misc/water/AbstractMethod.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/water/AbstractMethod.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/water/AbstractMethod.java index e634ffb90..150459850 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/water/AbstractMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/water/AbstractMethod.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.water; +package net.momirealms.customcrops.api.misc.water; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.action.ActionManager; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/water/FillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/misc/water/FillMethod.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/water/FillMethod.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/water/FillMethod.java index 0bc43f108..a514ae2d2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/water/FillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/water/FillMethod.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.water; +package net.momirealms.customcrops.api.misc.water; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/WaterBar.java b/api/src/main/java/net/momirealms/customcrops/api/misc/water/WaterBar.java similarity index 95% rename from api/src/main/java/net/momirealms/customcrops/api/misc/WaterBar.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/water/WaterBar.java index 87339d622..db0429842 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/misc/WaterBar.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/water/WaterBar.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.misc; +package net.momirealms.customcrops.api.misc.water; public record WaterBar(String left, String empty, String full, String right) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/water/WateringMethod.java b/api/src/main/java/net/momirealms/customcrops/api/misc/water/WateringMethod.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/water/WateringMethod.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/water/WateringMethod.java index a54918745..5fccaf515 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/water/WateringMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/water/WateringMethod.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.water; +package net.momirealms.customcrops.api.misc.water; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; 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 4e6b72712..aa3fb72c1 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 @@ -1,3 +1,20 @@ +/* + * 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.requirement; import dev.dejvokep.boostedyaml.block.implementation.Section; diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/DummyCancellable.java b/api/src/main/java/net/momirealms/customcrops/api/util/DummyCancellable.java new file mode 100644 index 000000000..fd47f9ac8 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/util/DummyCancellable.java @@ -0,0 +1,35 @@ +/* + * 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.util; + +import org.bukkit.event.Cancellable; + +public class DummyCancellable implements Cancellable { + + private boolean cancelled; + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/FakeCancellable.java b/api/src/main/java/net/momirealms/customcrops/api/util/FakeCancellable.java deleted file mode 100644 index 3202bc7fb..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/util/FakeCancellable.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.momirealms.customcrops.api.util; - -import org.bukkit.event.Cancellable; - -public class FakeCancellable implements Cancellable { - - private boolean cancelled; - - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public void setCancelled(boolean cancel) { - this.cancelled = cancel; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/PluginUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/PluginUtils.java index 982e7ca78..bab29e7b3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/util/PluginUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/PluginUtils.java @@ -1,3 +1,20 @@ +/* + * 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.util; import org.bukkit.Bukkit; diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java index 860ac8f72..b67834eb8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/util/StringUtils.java @@ -1,3 +1,20 @@ +/* + * 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.util; public class StringUtils { diff --git a/common/src/main/java/net/momirealms/customcrops/common/annotation/DoNotUse.java b/common/src/main/java/net/momirealms/customcrops/common/annotation/DoNotUse.java index ef22d08d7..4af0862b7 100644 --- a/common/src/main/java/net/momirealms/customcrops/common/annotation/DoNotUse.java +++ b/common/src/main/java/net/momirealms/customcrops/common/annotation/DoNotUse.java @@ -1,3 +1,20 @@ +/* + * 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.common.annotation; import org.jetbrains.annotations.ApiStatus; diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/Key.java b/common/src/main/java/net/momirealms/customcrops/common/util/Key.java index 2835d74d8..17488d907 100644 --- a/common/src/main/java/net/momirealms/customcrops/common/util/Key.java +++ b/common/src/main/java/net/momirealms/customcrops/common/util/Key.java @@ -1,3 +1,20 @@ +/* + * 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.common.util; public class Key { diff --git a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java index b29042f67..6dbaaf11a 100644 --- a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java +++ b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.integration.adaptor.asp_r1; import com.flowpowered.nbt.*; diff --git a/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java index 217383d14..7b611cd17 100644 --- a/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java +++ b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java @@ -1,9 +1,26 @@ +/* + * 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.bukkit.integration.custom.itemsadder_r1; import dev.lone.itemsadder.api.Events.*; import net.momirealms.customcrops.api.core.AbstractCustomEventListener; import net.momirealms.customcrops.api.core.AbstractItemManager; -import net.momirealms.customcrops.api.util.FakeCancellable; +import net.momirealms.customcrops.api.util.DummyCancellable; import org.bukkit.event.EventHandler; import org.bukkit.inventory.EquipmentSlot; @@ -73,7 +90,7 @@ public void onPlaceFurniture(FurniturePlaceSuccessEvent event) { event.getNamespacedID(), EquipmentSlot.HAND, event.getPlayer().getInventory().getItemInMainHand(), - new FakeCancellable() + new DummyCancellable() ); } } diff --git a/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderProvider.java b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderProvider.java index 23f0f439c..3d42b973d 100644 --- a/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderProvider.java +++ b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderProvider.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.integration.custom.itemsadder_r1; import dev.lone.itemsadder.api.CustomBlock; diff --git a/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenListener.java b/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenListener.java index a9a9e306b..4f957e8ae 100644 --- a/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenListener.java +++ b/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenListener.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.integration.custom.oraxen_r1; import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; diff --git a/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenProvider.java b/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenProvider.java index 6be0cb5f5..a491ababd 100644 --- a/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenProvider.java +++ b/compatibility-oraxen-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r1/OraxenProvider.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.integration.custom.oraxen_r1; import io.th0rgal.oraxen.api.OraxenBlocks; diff --git a/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenListener.java b/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenListener.java index c6a0b2c7b..b5c794545 100644 --- a/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenListener.java +++ b/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenListener.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.integration.custom.oraxen_r2; import io.th0rgal.oraxen.api.events.custom_block.noteblock.OraxenNoteBlockBreakEvent; diff --git a/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenProvider.java b/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenProvider.java index 7999c173c..81293d198 100644 --- a/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenProvider.java +++ b/compatibility-oraxen-r2/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/oraxen_r2/OraxenProvider.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.integration.custom.oraxen_r2; import io.th0rgal.oraxen.api.OraxenBlocks; diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/papi/CustomCropsPapi.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/papi/CustomCropsPapi.java index 91c836ba8..05b9aa1e4 100644 --- a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/papi/CustomCropsPapi.java +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/papi/CustomCropsPapi.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.integration.papi; import me.clip.placeholderapi.expansion.PlaceholderExpansion; 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 852394a39..3882e21ff 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java index a09aed243..c4d0e3072 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.action; import dev.dejvokep.boostedyaml.block.implementation.Section; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java index d6cd17256..408d23de8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.action; import dev.dejvokep.boostedyaml.block.implementation.Section; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java index 81ab091b9..f4d3639ed 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.config; import dev.dejvokep.boostedyaml.YamlDocument; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java index 092204074..26fa0ed3a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -33,7 +33,7 @@ import net.momirealms.customcrops.api.core.item.FertilizerType; import net.momirealms.customcrops.api.core.item.WateringCanConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; -import net.momirealms.customcrops.api.misc.WaterBar; +import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.requirement.RequirementManager; import net.momirealms.customcrops.common.util.Pair; 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 c40d81522..becf60bb4 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 @@ -1,3 +1,20 @@ +/* + * 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.bukkit.integration.adaptor; import com.flowpowered.nbt.CompoundMap; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemFactory.java index 7b8ce3942..588dceb0f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemFactory.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/BukkitItemFactory.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.item; import com.saicone.rtag.RtagItem; 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 818ffa5f6..3241abe12 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 @@ -1,3 +1,20 @@ +/* + * 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.bukkit.item; import net.kyori.adventure.key.Key; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/ComponentItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/ComponentItemFactory.java index fb6ad1133..e8b8700f4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/ComponentItemFactory.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/ComponentItemFactory.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.item; import com.saicone.rtag.RtagItem; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/UniversalItemFactory.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/UniversalItemFactory.java index 5cfc346c1..8b59ca58b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/UniversalItemFactory.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/item/UniversalItemFactory.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.item; import com.saicone.rtag.RtagItem; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/BlockRequirementManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/BlockRequirementManager.java index 23e247d64..f33c3a09b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/BlockRequirementManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/BlockRequirementManager.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.requirement; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java index 204925599..fa16d8648 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.requirement; import dev.dejvokep.boostedyaml.block.implementation.Section; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/DummyTask.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/DummyTask.java index 2c98218f4..8744895b0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/DummyTask.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/scheduler/DummyTask.java @@ -1,3 +1,20 @@ +/* + * 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.bukkit.scheduler; import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; 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 4df51036b..e4bb997f5 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 @@ -1,3 +1,20 @@ +/* + * 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.bukkit.world; import dev.dejvokep.boostedyaml.YamlDocument; From 139cd9649d07dfd70af87da36c9579928978c7de Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sun, 1 Sep 2024 01:39:51 +0800 Subject: [PATCH 067/329] implement watering can --- .../customcrops/api/context/ContextKeys.java | 3 + .../api/core/AbstractCustomEventListener.java | 3 +- .../customcrops/api/core/ConfigManager.java | 2 +- .../api/core/InteractionResult.java | 3 +- .../customcrops/api/core/ItemManager.java | 3 +- .../customcrops/api/core/Registries.java | 1 + .../api/core/SynchronizedCompoundMap.java | 2 +- .../customcrops/api/core/block/BoneMeal.java | 2 +- .../customcrops/api/core/block/CropBlock.java | 10 +- .../api/core/block/CrowAttack.java | 2 +- .../customcrops/api/core/block/PotBlock.java | 13 +- .../api/core/block/VariationData.java | 2 +- .../api/core/item/FertilizerItem.java | 16 +-- .../api/core/item/FertilizerType.java | 2 +- .../customcrops/api/core/item/SeedItem.java | 40 +++--- .../api/core/item/SprinklerItem.java | 10 +- .../api/core/item/WateringCanItem.java | 121 +++++++++++------- .../customcrops/api/core/world/BlockPos.java | 2 +- .../customcrops/api/core/world/ChunkPos.java | 2 +- .../api/core/world/CustomCropsChunk.java | 2 +- .../api/core/world/CustomCropsRegion.java | 2 +- .../api/core/world/CustomCropsSection.java | 2 +- .../api/core/world/CustomCropsWorld.java | 2 +- .../customcrops/api/core/world/DataBlock.java | 2 +- .../api/core/world/DelayedTickTask.java | 2 +- .../customcrops/api/core/world/RegionPos.java | 2 +- .../customcrops/api/core/world/Season.java | 2 +- .../api/core/world/SerializableChunk.java | 2 +- .../api/core/world/SerializableSection.java | 2 +- .../api/core/world/WorldExtraData.java | 2 +- .../api/core/world/WorldSetting.java | 2 +- .../api/event/BoneMealUseEvent.java | 2 +- .../customcrops/api/event/CropBreakEvent.java | 2 +- .../api/event/CropInteractEvent.java | 2 +- .../customcrops/api/event/CropPlantEvent.java | 2 +- .../api/event/FertilizerUseEvent.java | 2 +- .../api/event/GreenhouseGlassBreakEvent.java | 2 +- .../event/GreenhouseGlassInteractEvent.java | 2 +- .../api/event/GreenhouseGlassPlaceEvent.java | 2 +- .../customcrops/api/event/PotBreakEvent.java | 2 +- .../customcrops/api/event/PotFillEvent.java | 2 +- .../api/event/PotInteractEvent.java | 2 +- .../customcrops/api/event/PotPlaceEvent.java | 2 +- .../api/event/ScarecrowBreakEvent.java | 2 +- .../api/event/ScarecrowInteractEvent.java | 2 +- .../api/event/ScarecrowPlaceEvent.java | 2 +- .../api/event/SprinklerBreakEvent.java | 2 +- .../api/event/SprinklerFillEvent.java | 2 +- .../api/event/SprinklerInteractEvent.java | 2 +- .../api/event/SprinklerPlaceEvent.java | 2 +- .../api/event/WateringCanFillEvent.java | 2 +- .../api/event/WateringCanWaterPotEvent.java | 2 +- .../event/WateringCanWaterSprinklerEvent.java | 2 +- .../api/misc/water/AbstractMethod.java | 2 +- .../api/misc/water/FillMethod.java | 2 +- .../customcrops/api/misc/water/WaterBar.java | 2 +- .../api/misc/water/WateringMethod.java | 2 +- build.gradle.kts | 21 ++- .../bukkit/config/BukkitConfigManager.java | 1 + .../customcrops/bukkit/config/ConfigType.java | 4 +- .../bukkit/item/BukkitItemManager.java | 13 +- .../bukkit/misc/HologramManager.java | 2 +- 62 files changed, 210 insertions(+), 146 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java index 9c69bb428..da123e3a2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java @@ -30,6 +30,9 @@ public class ContextKeys { public static final ContextKeys LOCATION = of("location", Location.class); + public static final ContextKeys WATER_BAR = of("water_bar", String.class); + public static final ContextKeys CURRENT_WATER = of("current", Integer.class); + public static final ContextKeys STORAGE = of("storage", Integer.class); public static final ContextKeys X = of("x", Integer.class); public static final ContextKeys Y = of("y", Integer.class); public static final ContextKeys Z = of("z", Integer.class); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java index 172f78367..58f57d21b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -69,7 +69,8 @@ public void onInteractAir(PlayerInteractEvent event) { return; this.itemManager.handlePlayerInteractAir( event.getPlayer(), - event.getHand(), event.getItem() + event.getHand(), + event.getItem() ); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java index 915614c46..7d03bc561 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -57,7 +57,7 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable { protected boolean syncSeasons; protected String referenceWorld; - protected String[] itemDetectOrder; + protected String[] itemDetectOrder = new String[0]; protected double[] defaultQualityRatio; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java b/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java index 8c5c86b5d..8cf408a4b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/InteractionResult.java @@ -19,7 +19,6 @@ public enum InteractionResult { - SUCCESS, - FAIL, + COMPLETE, PASS } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java index e667237c8..41bfacccb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.core; import net.momirealms.customcrops.common.item.Item; +import net.momirealms.customcrops.common.plugin.feature.Reloadable; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.entity.Entity; @@ -26,7 +27,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public interface ItemManager { +public interface ItemManager extends Reloadable { void place(@NotNull Location location, @NotNull ExistenceForm form, @NotNull String id, FurnitureRotation rotation); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java index d18943297..d9a7884ab 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java @@ -56,4 +56,5 @@ public class Registries { public static final ClearableRegistry ITEM_TO_FERTILIZER = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_fertilizer")); public static final ClearableRegistry ITEM_TO_POT = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_pot")); public static final ClearableRegistry ITEM_TO_SPRINKLER = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_sprinkler")); + public static final ClearableRegistry ITEM_TO_WATERING_CAN = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_wateringcan")); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java index 55fd9797f..e0a3dbf3b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java index 2dc206dad..d74f05fb8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 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 616d490a5..4b74f9a21 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 @@ -21,6 +21,7 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.item.Fertilizer; import net.momirealms.customcrops.api.core.item.FertilizerConfig; @@ -35,6 +36,7 @@ import net.momirealms.customcrops.api.event.CropInteractEvent; import net.momirealms.customcrops.api.requirement.RequirementManager; import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.PlayerUtils; import org.bukkit.GameMode; import org.bukkit.Location; @@ -185,6 +187,7 @@ public void onInteract(WrappedInteractEvent event) { String blockBelowID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(potLocation.getBlock()); PotConfig potConfig = Registries.ITEM_TO_POT.get(blockBelowID); if (potConfig != null) { + context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(potLocation)); PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); assert potBlock != null; // fix or get data @@ -193,6 +196,7 @@ public void onInteract(WrappedInteractEvent event) { return; } + context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); if (point < cropConfig.maxPoints()) { for (BoneMeal boneMeal : cropConfig.boneMeals()) { if (boneMeal.requiredItem().equals(event.itemID()) && boneMeal.amountOfRequiredItem() <= itemInHand.getAmount()) { @@ -231,6 +235,7 @@ public void onInteract(WrappedInteractEvent event) { Objects.requireNonNull(afterStage); Context blockContext = Context.block(state); + blockContext.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); for (int i = point + 1; i <= afterPoints; i++) { CropStageConfig stage = cropConfig.stageByPoint(i); if (stage != null) { @@ -307,9 +312,10 @@ private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Po } Context context = Context.block(state); + Location bukkitLocation = location.toLocation(bukkitWorld); + context.arg(ContextKeys.LOCATION, bukkitLocation); for (DeathCondition deathCondition : config.deathConditions()) { if (deathCondition.isMet(context)) { - Location bukkitLocation = location.toLocation(bukkitWorld); BukkitCustomCropsPlugin.getInstance().getScheduler().sync().runLater(() -> { FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); world.removeBlockState(location); @@ -375,8 +381,6 @@ private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Po tempPoints = after.point() - 1; } - Location bukkitLocation = location.toLocation(bukkitWorld); - final String finalPreStage = preStage; final String finalAfterStage = afterStage; final ExistenceForm finalAfterForm = afterForm; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java index cd06a01eb..bb95d4fc9 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 4a125941b..82672f247 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -21,6 +21,7 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.item.Fertilizer; import net.momirealms.customcrops.api.core.item.FertilizerConfig; @@ -34,6 +35,7 @@ import net.momirealms.customcrops.api.event.*; import net.momirealms.customcrops.api.requirement.RequirementManager; import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.PlayerUtils; import net.momirealms.customcrops.api.util.StringUtils; import org.bukkit.GameMode; @@ -220,6 +222,8 @@ public void onInteract(WrappedInteractEvent event) { final Player player = event.player(); Context context = Context.player(player); + context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); + // check use requirements if (!RequirementManager.isSatisfied(context, potConfig.useRequirements())) { return; @@ -258,6 +262,9 @@ protected boolean tryWateringPot(Player player, Context context, CustomC } } } + if (addWater(state, method.amountOfWater())) { + updateBlockAppearance(potLocation, potConfig, true, fertilizers(state)); + } method.triggerActions(context); ActionManager.trigger(context, potConfig.addWaterActions()); } @@ -361,7 +368,11 @@ private void tickPot(CustomCropsBlockState state, CustomCropsWorld world, Pos boolean fertilizerChanged = tickFertilizer(state); if (fertilizerChanged || waterChanged) { - updateBlockAppearance(location.toLocation(bukkitWorld), config, hasNaturalWater, fertilizers(state)); + boolean finalHasNaturalWater = hasNaturalWater; + Location bukkitLocation = location.toLocation(bukkitWorld); + BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run(() -> { + updateBlockAppearance(bukkitLocation, config, finalHasNaturalWater, fertilizers(state)); + }, bukkitLocation); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java index dd8f30c43..8afd76364 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java index 82bdfcb8c..10505bdee 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java @@ -53,7 +53,7 @@ public FertilizerItem() { public InteractionResult interactAt(WrappedInteractEvent event) { FertilizerConfig fertilizerConfig = Registries.FERTILIZER.get(event.itemID()); if (fertilizerConfig == null) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } final Player player = event.player(); @@ -77,14 +77,14 @@ public InteractionResult interactAt(WrappedInteractEvent event) { // check pot whitelist if (!fertilizerConfig.whitelistPots().contains(potConfig.id())) { ActionManager.trigger(context, fertilizerConfig.wrongPotActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // check requirements if (!RequirementManager.isSatisfied(context, fertilizerConfig.requirements())) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } if (!RequirementManager.isSatisfied(context, potConfig.useRequirements())) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // check "before-plant" if (fertilizerConfig.beforePlant()) { @@ -94,7 +94,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { CustomCropsBlockState blockState = state.get(); if (blockState.type() instanceof CropBlock) { ActionManager.trigger(context, fertilizerConfig.beforePlantActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } } } @@ -108,12 +108,12 @@ public InteractionResult interactAt(WrappedInteractEvent event) { .build(); CustomCropsBlockState potState = potBlock.fixOrGetState(world, Pos3.from(targetLocation), potConfig, event.relatedID()); if (!potBlock.canApplyFertilizer(potState,fertilizer)) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // trigger event FertilizerUseEvent useEvent = new FertilizerUseEvent(player, itemInHand, fertilizer, targetLocation, potState, event.hand(), potConfig); if (EventUtils.fireAndCheckCancel(useEvent)) - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; // add the fertilizer if (potBlock.addFertilizer(potState, fertilizer)) { potBlock.updateBlockAppearance(targetLocation, potState, potBlock.fertilizers(potState)); @@ -122,7 +122,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { itemInHand.setAmount(itemInHand.getAmount() - 1); } ActionManager.trigger(context, fertilizerConfig.useActions()); - return InteractionResult.SUCCESS; + return InteractionResult.COMPLETE; } return InteractionResult.PASS; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java index 46426524e..0868408b1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java index 9cd844ae0..3ddb0f5f9 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java @@ -45,12 +45,11 @@ import org.bukkit.inventory.ItemStack; import java.util.Collection; -import java.util.Map; public class SeedItem extends AbstractCustomCropsItem { public SeedItem() { - super(BuiltInBlockMechanics.CROP.key()); + super(BuiltInItemMechanics.SEED.key()); } @Override @@ -60,11 +59,10 @@ public InteractionResult interactAt(WrappedInteractEvent event) { if (potConfig == null) return InteractionResult.PASS; // check if the crop exists CropConfig cropConfig = Registries.SEED_TO_CROP.get(event.itemID()); - if (cropConfig == null) return InteractionResult.FAIL; + if (cropConfig == null) return InteractionResult.COMPLETE; // check the block face if (event.clickedBlockFace() != BlockFace.UP) return InteractionResult.PASS; - final Player player = event.player(); Context context = Context.player(player); // check pot whitelist @@ -73,20 +71,21 @@ public InteractionResult interactAt(WrappedInteractEvent event) { } // check plant requirements if (!RequirementManager.isSatisfied(context, cropConfig.plantRequirements())) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // check if the block is empty - if (!suitableForSeed(event.location())) { - return InteractionResult.FAIL; + Location seedLocation = event.location().add(0, 1, 0); + if (!suitableForSeed(seedLocation)) { + return InteractionResult.COMPLETE; } CustomCropsWorld world = event.world(); - Location seedLocation = event.location().add(0, 1, 0); + Pos3 pos3 = Pos3.from(seedLocation); // check limitation if (world.setting().cropPerChunk() >= 0) { if (world.testChunkLimitation(pos3, CropBlock.class, world.setting().cropPerChunk())) { ActionManager.trigger(context, cropConfig.reachLimitActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } } final ItemStack itemInHand = event.itemInHand(); @@ -97,24 +96,17 @@ public InteractionResult interactAt(WrappedInteractEvent event) { // trigger event CropPlantEvent plantEvent = new CropPlantEvent(player, itemInHand, event.hand(), seedLocation, cropConfig, state, 0); if (EventUtils.fireAndCheckCancel(plantEvent)) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } int point = plantEvent.getPoint(); - int temp = point; - ExistenceForm form = null; - String stageID = null; - while (temp >= 0) { - Map.Entry entry = cropConfig.getFloorStageEntry(temp); - CropStageConfig stageConfig = entry.getValue(); - if (stageConfig.stageID() != null) { - form = stageConfig.existenceForm(); - stageID = stageConfig.stageID(); - break; - } - temp = stageConfig.point() - 1; + CropStageConfig stageConfig = cropConfig.stageWithModelByPoint(point); + if (stageConfig == null) { + return InteractionResult.COMPLETE; } + String stageID = stageConfig.stageID(); + ExistenceForm form = stageConfig.existenceForm(); if (stageID == null || form == null) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // reduce item if (player.getGameMode() != GameMode.CREATIVE) @@ -133,7 +125,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { context.arg(ContextKeys.SLOT, event.hand()); // trigger plant actions ActionManager.trigger(context, cropConfig.plantActions()); - return InteractionResult.SUCCESS; + return InteractionResult.COMPLETE; } private boolean suitableForSeed(Location location) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java index 93cf1ca26..96ecae824 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java @@ -56,7 +56,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { return InteractionResult.PASS; SprinklerConfig config = Registries.SPRINKLER.get(event.itemID()); if (config == null) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } Block clicked = event.location().getBlock(); @@ -79,7 +79,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { Context context = Context.player(player); // check requirements if (!RequirementManager.isSatisfied(context, config.placeRequirements())) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } final CustomCropsWorld world = event.world(); @@ -88,7 +88,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { if (world.setting().sprinklerPerChunk() >= 0) { if (world.testChunkLimitation(pos3, SprinklerBlock.class, world.setting().sprinklerPerChunk())) { ActionManager.trigger(context, config.reachLimitActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } } // generate state @@ -99,7 +99,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { // trigger event SprinklerPlaceEvent placeEvent = new SprinklerPlaceEvent(player, itemInHand, event.hand(), targetLocation.clone(), config, state); if (EventUtils.fireAndCheckCancel(placeEvent)) - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; // clear replaceable block targetLocation.getBlock().setType(Material.AIR, false); if (player.getGameMode() != GameMode.CREATIVE) @@ -114,7 +114,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { ); }); ActionManager.trigger(context, config.placeActions()); - return InteractionResult.SUCCESS; + return InteractionResult.COMPLETE; } private boolean suitableForSprinkler(Location location) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java index 2d5218c6a..647a650cc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java @@ -49,6 +49,8 @@ import org.bukkit.block.data.Waterlogged; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.util.RayTraceResult; +import org.bukkit.util.Vector; import java.util.ArrayList; import java.util.List; @@ -71,7 +73,7 @@ public void setCurrentWater(ItemStack itemStack, WateringCanConfig config, int w Item wrapped = BukkitCustomCropsPlugin.getInstance().getItemManager().wrap(itemStack); int realWater = Math.min(config.storage(), water); if (!config.infinite()) { - wrapped.setTag("CustomCrops", "water", realWater); + wrapped.setTag(realWater, "CustomCrops", "water"); wrapped.maxDamage().ifPresent(max -> { if (max <= 0) return; int damage = (int) (max * (((double) config.storage() - realWater) / config.storage())); @@ -104,23 +106,27 @@ public void setCurrentWater(ItemStack itemStack, WateringCanConfig config, int w @Override public void interactAir(WrappedInteractAirEvent event) { - WateringCanConfig config = Registries.WATERING_CAN.get(event.itemID()); + WateringCanConfig config = Registries.ITEM_TO_WATERING_CAN.get(event.itemID()); if (config == null) return; final Player player = event.player();; Context context = Context.player(player); // check requirements - if (!RequirementManager.isSatisfied(context, config.requirements())) { + if (!RequirementManager.isSatisfied(context, config.requirements())) return; - } // ignore infinite if (config.infinite()) return; - // get target block - Block targetBlock = player.getTargetBlockExact(5, FluidCollisionMode.ALWAYS); + RayTraceResult result = player.getWorld().rayTraceBlocks(player.getEyeLocation(), player.getLocation().getDirection(), 5, FluidCollisionMode.ALWAYS); + if (result == null) + return; + Block targetBlock = result.getHitBlock(); if (targetBlock == null) return; + final Vector vector = result.getHitPosition(); + // for old config compatibility + context.arg(ContextKeys.LOCATION, new Location(player.getWorld(), vector.getX() - 0.5,vector.getY() - 1, vector.getZ() - 0.5)); final ItemStack itemInHand = event.itemInHand(); int water = getCurrentWater(itemInHand); @@ -140,6 +146,10 @@ public void interactAir(WrappedInteractAirEvent event) { WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, event.hand(), itemInHand, targetBlock.getLocation(), config, method); if (EventUtils.fireAndCheckCancel(fillEvent)) return; + int current = Math.min(water + method.amountOfWater(), config.storage()); + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(config.waterBar()).map(bar -> bar.getWaterBar(current, config.storage())).orElse("")); + context.arg(ContextKeys.STORAGE, config.storage()); + context.arg(ContextKeys.CURRENT_WATER, current); setCurrentWater(itemInHand, config, water + method.amountOfWater(), context); method.triggerActions(context); ActionManager.trigger(context, config.addWaterActions()); @@ -151,16 +161,16 @@ public void interactAir(WrappedInteractAirEvent event) { @Override public InteractionResult interactAt(WrappedInteractEvent event) { - WateringCanConfig wateringCanConfig = Registries.WATERING_CAN.get(event.itemID()); + WateringCanConfig wateringCanConfig = Registries.ITEM_TO_WATERING_CAN.get(event.itemID()); if (wateringCanConfig == null) - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; final Player player = event.player(); final Context context = Context.player(player); // check watering can requirements if (!RequirementManager.isSatisfied(context, wateringCanConfig.requirements())) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } final CustomCropsWorld world = event.world(); @@ -175,21 +185,21 @@ public InteractionResult interactAt(WrappedInteractEvent event) { if (sprinklerConfig != null) { // ignore infinite sprinkler if (sprinklerConfig.infinite()) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // check requirements if (!RequirementManager.isSatisfied(context, sprinklerConfig.useRequirements())) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // check water if (waterInCan <= 0 && !wateringCanConfig.infinite()) { ActionManager.trigger(context, wateringCanConfig.runOutOfWaterActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // check whitelist if (!wateringCanConfig.whitelistSprinklers().contains(sprinklerConfig.id())) { ActionManager.trigger(context, wateringCanConfig.wrongSprinklerActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } SprinklerBlock sprinklerBlock = (SprinklerBlock) BuiltInBlockMechanics.SPRINKLER.mechanic(); @@ -198,14 +208,19 @@ public InteractionResult interactAt(WrappedInteractEvent event) { // check full if (sprinklerBlock.water(sprinklerState) >= sprinklerConfig.storage()) { ActionManager.trigger(context, sprinklerConfig.fullWaterActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // trigger event WateringCanWaterSprinklerEvent waterSprinklerEvent = new WateringCanWaterSprinklerEvent(player, itemInHand, event.hand(), wateringCanConfig, sprinklerConfig, sprinklerState, targetLocation); if (EventUtils.fireAndCheckCancel(waterSprinklerEvent)) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } + + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(wateringCanConfig.waterBar()).map(bar -> bar.getWaterBar(waterInCan - 1, wateringCanConfig.storage())).orElse("")); + context.arg(ContextKeys.STORAGE, wateringCanConfig.storage()); + context.arg(ContextKeys.CURRENT_WATER, waterInCan - 1); + // add water if (sprinklerBlock.addWater(sprinklerState, sprinklerConfig, wateringCanConfig.wateringAmount())) { if (!sprinklerConfig.threeDItem().equals(sprinklerConfig.threeDItemWithWater())) { @@ -215,26 +230,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { ActionManager.trigger(context, wateringCanConfig.consumeWaterActions()); setCurrentWater(itemInHand, wateringCanConfig, waterInCan - 1, context); - return InteractionResult.SUCCESS; - } - - // try filling the watering can - for (FillMethod method : wateringCanConfig.fillMethods()) { - if (method.getID().equals(event.relatedID())) { - if (method.checkRequirements(context)) { - if (waterInCan >= wateringCanConfig.storage()) { - ActionManager.trigger(context, wateringCanConfig.fullActions()); - return InteractionResult.FAIL; - } - WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, event.hand(), itemInHand, targetLocation, wateringCanConfig, method); - if (EventUtils.fireAndCheckCancel(fillEvent)) - return InteractionResult.FAIL; - setCurrentWater(itemInHand, wateringCanConfig, waterInCan + method.amountOfWater(), context); - method.triggerActions(context); - ActionManager.trigger(context, wateringCanConfig.addWaterActions()); - } - return InteractionResult.SUCCESS; - } + return InteractionResult.COMPLETE; } // if the clicked block is a crop, correct the target block @@ -254,12 +250,12 @@ public InteractionResult interactAt(WrappedInteractEvent event) { // check whitelist if (!wateringCanConfig.whitelistPots().contains(potConfig.id())) { ActionManager.trigger(context, wateringCanConfig.wrongPotActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } // check water if (waterInCan <= 0 && !wateringCanConfig.infinite()) { ActionManager.trigger(context, wateringCanConfig.runOutOfWaterActions()); - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } World bukkitWorld = targetLocation.getWorld(); @@ -267,26 +263,65 @@ public InteractionResult interactAt(WrappedInteractEvent event) { WateringCanWaterPotEvent waterPotEvent = new WateringCanWaterPotEvent(player, itemInHand, event.hand(), wateringCanConfig, potConfig, pots); if (EventUtils.fireAndCheckCancel(waterPotEvent)) { - return InteractionResult.FAIL; + return InteractionResult.COMPLETE; } + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(wateringCanConfig.waterBar()).map(bar -> bar.getWaterBar(waterInCan - 1, wateringCanConfig.storage())).orElse("")); + context.arg(ContextKeys.STORAGE, wateringCanConfig.storage()); + context.arg(ContextKeys.CURRENT_WATER, waterInCan - 1); + PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); for (Pair pair : waterPotEvent.getPotWithIDs()) { CustomCropsBlockState potState = potBlock.fixOrGetState(world,pair.left(), potConfig, pair.right()); + Location temp = pair.left().toLocation(bukkitWorld); if (potBlock.addWater(potState, potConfig, wateringCanConfig.wateringAmount())) { - Location temp = pair.left().toLocation(bukkitWorld); potBlock.updateBlockAppearance(temp, potConfig, true, potBlock.fertilizers(potState)); - context.arg(ContextKeys.LOCATION, temp); - ActionManager.trigger(context, potConfig.addWaterActions()); } + context.arg(ContextKeys.LOCATION, temp); + ActionManager.trigger(context, potConfig.addWaterActions()); } ActionManager.trigger(context, wateringCanConfig.consumeWaterActions()); setCurrentWater(itemInHand, wateringCanConfig, waterInCan - 1, context); - return InteractionResult.SUCCESS; + return InteractionResult.COMPLETE; + } + + RayTraceResult result = player.getWorld().rayTraceBlocks(player.getEyeLocation(), player.getLocation().getDirection(), 5, FluidCollisionMode.ALWAYS); + if (result == null) + return InteractionResult.COMPLETE; + Block targetBlock = result.getHitBlock(); + if (targetBlock == null) + return InteractionResult.COMPLETE; + final Vector vector = result.getHitPosition(); + // for old config compatibility + context.arg(ContextKeys.LOCATION, new Location(player.getWorld(), vector.getX() - 0.5,vector.getY() - 1, vector.getZ() - 0.5)); + String blockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(targetBlock); + if (targetBlock.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { + blockID = "WATER"; + } + for (FillMethod method : wateringCanConfig.fillMethods()) { + if (method.getID().equals(blockID)) { + if (method.checkRequirements(context)) { + if (waterInCan >= wateringCanConfig.storage()) { + ActionManager.trigger(context, wateringCanConfig.fullActions()); + return InteractionResult.COMPLETE; + } + WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, event.hand(), itemInHand, targetBlock.getLocation(), wateringCanConfig, method); + if (EventUtils.fireAndCheckCancel(fillEvent)) + return InteractionResult.COMPLETE; + int current = Math.min(waterInCan + method.amountOfWater(), wateringCanConfig.storage()); + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(wateringCanConfig.waterBar()).map(bar -> bar.getWaterBar(current, wateringCanConfig.storage())).orElse("")); + context.arg(ContextKeys.STORAGE, wateringCanConfig.storage()); + context.arg(ContextKeys.CURRENT_WATER, current); + setCurrentWater(itemInHand, wateringCanConfig, waterInCan + method.amountOfWater(), context); + method.triggerActions(context); + ActionManager.trigger(context, wateringCanConfig.addWaterActions()); + } + return InteractionResult.COMPLETE; + } } - return InteractionResult.PASS; + return InteractionResult.COMPLETE; } public ArrayList> potInRange(World world, Pos3 pos3, int width, int length, float yaw, PotConfig config) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java index c3bf17a03..d91a1e106 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java index fe43a88ff..044f0171f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java index 9d7894c0d..16f66ccbc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java index d8f830215..1746bcf12 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java index e44d4a492..0a6b22e58 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java index f4438ec9c..33e5cc446 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java index f063ffb08..227a7cecd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/DelayedTickTask.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/DelayedTickTask.java index 2526dfdf3..99cece555 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/DelayedTickTask.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/DelayedTickTask.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java index ac0cf690a..e4d530cad 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/Season.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/Season.java index f6a0398e2..8fbb4fcfc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/Season.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/Season.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableChunk.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableChunk.java index 4b9d496b1..441e4ae54 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableChunk.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableSection.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableSection.java index 5204eb2e3..6d170f787 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableSection.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/SerializableSection.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java index ecfa0c7c2..650eafc77 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java index 6df176de6..55737bbf9 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java index 17a8c8e48..37e401122 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java index 03b09d2b7..1d2daefd7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java index 8f89ee3df..636ec03ea 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java index 21d15d6f9..98379b697 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java index 85a276cd3..25b37926f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java index b89871692..b14a3a2c0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java index df0a46bcf..c8886effc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java index a5cfc215c..b77b30659 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java index d443f4ecd..1bf00d06e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java index 3d5141cd7..78379cef6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java index fc59d6c44..ebc2796e8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java index 23e2f94ee..d10666735 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java index 65b8ac192..43a153b52 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java index c84cddec0..e8f7acd3d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java index c55657d04..18f859d1b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java index dfa508d8b..dee448baf 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java index ace165e2d..08a64c118 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java index 7aaf4b2c2..f3d5da340 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java index e2b149408..f1cead1b3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java index 6dce329a5..ffadfaf9a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java index e1e45f2db..d504dc2dd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java index 5d31bbf9e..9cf0aa212 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/water/AbstractMethod.java b/api/src/main/java/net/momirealms/customcrops/api/misc/water/AbstractMethod.java index 150459850..ee1c2de84 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/misc/water/AbstractMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/water/AbstractMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/water/FillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/misc/water/FillMethod.java index a514ae2d2..4d8c5f13c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/misc/water/FillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/water/FillMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/water/WaterBar.java b/api/src/main/java/net/momirealms/customcrops/api/misc/water/WaterBar.java index db0429842..bf9207442 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/misc/water/WaterBar.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/water/WaterBar.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/water/WateringMethod.java b/api/src/main/java/net/momirealms/customcrops/api/misc/water/WateringMethod.java index 5fccaf515..bc7e37afd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/misc/water/WateringMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/water/WateringMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 diff --git a/build.gradle.kts b/build.gradle.kts index d72e8d7f6..a3ec23bc5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,4 @@ +import org.gradle.process.internal.ExecException import java.io.ByteArrayOutputStream plugins { @@ -32,18 +33,26 @@ subprojects { fun versionBanner(): String { val os = ByteArrayOutputStream() - project.exec { - commandLine = "git rev-parse --short=8 HEAD".split(" ") - standardOutput = os + try { + project.exec { + commandLine = "git rev-parse --short=8 HEAD".split(" ") + standardOutput = os + } + } catch (e: ExecException) { + return "Unknown" } return String(os.toByteArray()).trim() } fun builder(): String { val os = ByteArrayOutputStream() - project.exec { - commandLine = "git config user.name".split(" ") - standardOutput = os + try { + project.exec { + commandLine = "git config user.name".split(" ") + standardOutput = os + } + } catch (e: ExecException) { + return "Unknown" } return String(os.toByteArray()).trim() } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java index f4d3639ed..0049ef56e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -213,6 +213,7 @@ private void clearConfigs() { @Override public void registerWateringCanConfig(WateringCanConfig config) { Registries.WATERING_CAN.register(config.id(), config); + Registries.ITEM_TO_WATERING_CAN.register(config.itemID(), config); Registries.ITEMS.register(config.itemID(), BuiltInItemMechanics.WATERING_CAN.mechanic()); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java index 26fa0ed3a..3ebe695ff 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -66,7 +66,7 @@ public class ConfigType { .length(section.getInt("effective-range.length", 1)) .potWhitelist(new HashSet<>(section.getStringList("pot-whitelist"))) .sprinklerWhitelist(new HashSet<>(section.getStringList("sprinkler-whitelist"))) - .dynamicLore(section.getBoolean("dynamic-lore")) + .dynamicLore(section.getBoolean("dynamic-lore.enable")) .lore(section.getStringList("dynamic-lore.lore").stream().map(TextValue::auto).toList()) .fillMethods(manager.getFillMethods(section.getSection("fill-method"))) .requirements(BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class).parseRequirements(section.getSection("requirements"), true)) @@ -115,6 +115,7 @@ public class ConfigType { PotConfig config = PotConfig.builder() .id(id) + .storage(section.getInt("storage", 5)) .isRainDropAccepted(section.getBoolean("absorb-rainwater", false)) .isNearbyWaterAccepted(section.getBoolean("absorb-nearby-water", false)) .maxFertilizers(section.getInt("max-fertilizers", 1)) @@ -187,6 +188,7 @@ public class ConfigType { int point = Integer.parseInt(entry.getKey()); CropStageConfig.Builder builder = CropStageConfig.builder() .point(point) + .existenceForm(inner.contains("type") ? CustomForm.valueOf(inner.getString("type").toUpperCase(Locale.ENGLISH)).existenceForm() : form) .displayInfoOffset(inner.getDouble("hologram-offset-correction")) .stageID(inner.getString("model")) .breakRequirements(prm.parseRequirements(inner.getSection("requirements.break"), true)) 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 3241abe12..e65844826 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 @@ -80,6 +80,11 @@ public BukkitItemManager(BukkitCustomCropsPlugin plugin) { this.factory = BukkitItemFactory.create(plugin); } + @Override + public void load() { + this.resetItemDetectionOrder(); + } + @Override public void setCustomEventListener(@NotNull AbstractCustomEventListener listener) { Objects.requireNonNull(listener, "listener cannot be null"); @@ -392,7 +397,7 @@ public void handlePlayerInteractBlock(Player player, Block block, String blockID CustomCropsWorld world = optionalWorld.get(); WrappedInteractEvent wrapped = new WrappedInteractEvent(ExistenceForm.BLOCK, player, world, block.getLocation(), blockID, itemInHand, itemID, hand, blockFace, event); - handleInteractEvent(blockID, itemID, wrapped); + handleInteractEvent(blockID, wrapped); } @Override @@ -406,11 +411,11 @@ public void handlePlayerInteractFurniture(Player player, Location location, Stri CustomCropsWorld world = optionalWorld.get(); WrappedInteractEvent wrapped = new WrappedInteractEvent(ExistenceForm.FURNITURE, player, world, location, furnitureID, itemInHand, itemID, hand, null, event); - handleInteractEvent(furnitureID, itemID, wrapped); + handleInteractEvent(furnitureID, wrapped); } - private void handleInteractEvent(String blockID, String itemID, WrappedInteractEvent wrapped) { - CustomCropsItem customCropsItem = Registries.ITEMS.get(itemID); + private void handleInteractEvent(String blockID, WrappedInteractEvent wrapped) { + CustomCropsItem customCropsItem = Registries.ITEMS.get(wrapped.itemID()); if (customCropsItem != null) { InteractionResult result = customCropsItem.interactAt(wrapped); if (result != InteractionResult.PASS) diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java index 69db25463..04aa6f0b1 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * 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 From 37688940ca5c7ef7aa19ed40c05450717e2f94a4 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:39:25 +0800 Subject: [PATCH 068/329] Fix some logics --- .../api/action/AbstractActionManager.java | 100 +++++++++++++--- .../customcrops/api/context/ContextKeys.java | 2 + .../api/core/BuiltInItemMechanics.java | 2 +- .../customcrops/api/core/ConfigManager.java | 2 +- .../customcrops/api/core/RegistryAccess.java | 2 +- .../api/core/SimpleRegistryAccess.java | 2 +- .../core/block/AbstractCustomCropsBlock.java | 2 +- .../customcrops/api/core/block/CropBlock.java | 5 +- .../api/core/block/CustomCropsBlock.java | 2 +- .../customcrops/api/core/block/PotBlock.java | 3 +- .../customcrops/api/core/block/PotConfig.java | 2 +- .../api/core/block/PotConfigImpl.java | 2 +- .../api/core/block/ScarecrowBlock.java | 4 +- .../api/core/block/SprinklerBlock.java | 43 +++++-- .../api/core/block/SprinklerConfig.java | 2 +- .../api/core/block/SprinklerConfigImpl.java | 2 +- .../core/item/AbstractCustomCropsItem.java | 2 +- .../api/core/item/CustomCropsItem.java | 2 +- .../api/core/item/FertilizerItem.java | 8 +- .../customcrops/api/core/item/SeedItem.java | 6 +- .../api/core/item/SprinklerItem.java | 8 +- .../api/core/item/WateringCanConfig.java | 2 +- .../api/core/item/WateringCanConfigImpl.java | 2 +- .../api/core/item/WateringCanItem.java | 9 +- .../customcrops/api/event/PotFillEvent.java | 2 +- .../api/event/SprinklerFillEvent.java | 2 +- .../api}/misc/HologramManager.java | 43 +++---- .../adaptor/asp_r1/SlimeWorldAdaptorR1.java | 2 +- .../itemsadder_r1/ItemsAdderListener.java | 6 +- gradle.properties | 4 +- .../bukkit/BukkitCustomCropsPluginImpl.java | 5 +- .../bukkit/action/PlayerActionManager.java | 113 ++++++------------ .../bukkit/config/BukkitConfigManager.java | 9 +- .../customcrops/bukkit/config/ConfigType.java | 2 +- .../bukkit/item/BukkitItemManager.java | 1 + .../requirement/PlayerRequirementManager.java | 17 ++- 36 files changed, 248 insertions(+), 174 deletions(-) rename {plugin/src/main/java/net/momirealms/customcrops/bukkit => api/src/main/java/net/momirealms/customcrops/api}/misc/HologramManager.java (74%) diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java index b4f5fd59a..033a9d115 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java @@ -19,6 +19,7 @@ import dev.dejvokep.boostedyaml.block.implementation.Section; import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; @@ -32,6 +33,7 @@ import net.momirealms.customcrops.api.core.world.Pos3; import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent; import net.momirealms.customcrops.api.event.CropPlantEvent; +import net.momirealms.customcrops.api.misc.HologramManager; import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; import net.momirealms.customcrops.api.misc.value.MathValue; import net.momirealms.customcrops.api.misc.value.TextValue; @@ -40,7 +42,10 @@ import net.momirealms.customcrops.common.helper.AdventureHelper; import net.momirealms.customcrops.common.helper.VersionHelper; import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; -import net.momirealms.customcrops.common.util.*; +import net.momirealms.customcrops.common.util.ClassUtils; +import net.momirealms.customcrops.common.util.ListUtils; +import net.momirealms.customcrops.common.util.Pair; +import net.momirealms.customcrops.common.util.RandomUtils; import net.momirealms.sparrow.heart.SparrowHeart; import net.momirealms.sparrow.heart.feature.entity.FakeEntity; import net.momirealms.sparrow.heart.feature.entity.armorstand.FakeArmorStand; @@ -83,6 +88,7 @@ protected void registerBuiltInActions() { this.registerDropItemsAction(); this.registerLegacyDropItemsAction(); this.registerFakeItemAction(); + this.registerHologramAction(); this.registerPlantAction(); this.registerBreakAction(); } @@ -725,21 +731,84 @@ protected void registerLegacyDropItemsAction() { }, "drop-items"); } - private void registerFakeItemAction() { + protected void registerHologramAction() { + registerAction(((args, chance) -> { + if (args instanceof Section section) { + TextValue text = TextValue.auto(section.getString("text", "")); + MathValue duration = MathValue.auto(section.get("duration", 20)); + boolean other = section.getString("position", "other").equals("other"); + MathValue x = MathValue.auto(section.get("x", 0)); + MathValue y = MathValue.auto(section.get("y", 0)); + MathValue z = MathValue.auto(section.get("z", 0)); + boolean applyCorrection = section.getBoolean("apply-correction", false); + boolean onlyShowToOne = !section.getBoolean("visible-to-all", false); + int range = section.getInt("range", 32); + return context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Player owner = null; + if (context.holder() instanceof Player p) { + owner = p; + } + Location location = other ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : owner.getLocation().clone(); + Pos3 pos3 = Pos3.from(location).add(0,1,0); + location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + if (applyCorrection) { + Optional optionalState = optionalWorld.get().getBlockState(pos3); + if (optionalState.isPresent()) { + if (optionalState.get().type() instanceof CropBlock cropBlock) { + CropConfig config = cropBlock.config(optionalState.get()); + int point = cropBlock.point(optionalState.get()); + if (config != null) { + CropStageConfig stageConfig = config.stageWithModelByPoint(point); + location.add(0,stageConfig.displayInfoOffset(),0); + } + } + } + } + ArrayList viewers = new ArrayList<>(); + if (onlyShowToOne) { + if (owner == null) return; + viewers.add(owner); + } else { + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= range) { + viewers.add(player); + } + } + } + if (viewers.isEmpty()) return; + Component component = AdventureHelper.miniMessage(text.render(context)); + for (Player viewer : viewers) { + HologramManager.getInstance().showHologram(viewer, location, AdventureHelper.componentToJson(component), (int) (duration.evaluate(context) * 50)); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at hologram action which is expected to be `Section`"); + return Action.empty(); + } + }), "hologram"); + } + + protected void registerFakeItemAction() { registerAction(((args, chance) -> { if (args instanceof Section section) { String itemID = section.getString("item", ""); String[] split = itemID.split(":"); if (split.length >= 2) itemID = split[split.length - 1]; MathValue duration = MathValue.auto(section.get("duration", 20)); - boolean position = !section.getString("position", "player").equals("player"); + boolean other = section.getString("position", "other").equals("other"); MathValue x = MathValue.auto(section.get("x", 0)); MathValue y = MathValue.auto(section.get("y", 0)); MathValue z = MathValue.auto(section.get("z", 0)); MathValue yaw = MathValue.auto(section.get("yaw", 0)); - int range = section.getInt("range", 0); + int range = section.getInt("range", 32); + boolean visibleToAll = section.getBoolean("visible-to-all", true); boolean useItemDisplay = section.getBoolean("use-item-display", false); - boolean onlyShowToOne = !section.getBoolean("visible-to-all", true); String finalItemID = itemID; return context -> { if (Math.random() > chance) return; @@ -748,8 +817,8 @@ private void registerFakeItemAction() { if (context.holder() instanceof Player p) { owner = p; } - Location location = position ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : requireNonNull(owner).getLocation().clone(); - location.add(x.evaluate(context), y.evaluate(context) - 1, z.evaluate(context)); + Location location = other ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : requireNonNull(owner).getLocation().clone(); + location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); location.setPitch(0); location.setYaw((float) yaw.evaluate(context)); FakeEntity fakeEntity; @@ -765,19 +834,18 @@ private void registerFakeItemAction() { fakeEntity = armorStand; } ArrayList viewers = new ArrayList<>(); - if (onlyShowToOne) { - viewers.add(owner); - } else { - if (range > 0) { - for (Player player : location.getWorld().getPlayers()) { - if (LocationUtils.getDistance(player.getLocation(), location) <= range) { - viewers.add(player); - } + if (range > 0 && visibleToAll) { + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= range) { + viewers.add(player); } - } else { + } + } else { + if (owner != null) { viewers.add(owner); } } + if (viewers.isEmpty()) return; for (Player player : viewers) { fakeEntity.spawn(player); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java index da123e3a2..dbe6cf8fd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.context; import org.bukkit.Location; +import org.bukkit.entity.Player; import org.bukkit.inventory.EquipmentSlot; import java.util.Objects; @@ -41,6 +42,7 @@ public class ContextKeys { public static final ContextKeys SLOT = of("slot", EquipmentSlot.class); public static final ContextKeys TEMP_NEAR_PLAYER = of("near", String.class); public static final ContextKeys OFFLINE = of("offline", Boolean.class); + public static final ContextKeys PLAYER_INSTANCE = of("player_instance", Player.class); private final String key; private final Class type; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java index 97624fe15..4043dc01c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java @@ -17,8 +17,8 @@ package net.momirealms.customcrops.api.core; -import net.momirealms.customcrops.common.util.Key; import net.momirealms.customcrops.api.core.item.CustomCropsItem; +import net.momirealms.customcrops.common.util.Key; public class BuiltInItemMechanics { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java index 7d03bc561..7eec0d24f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -16,9 +16,9 @@ import net.momirealms.customcrops.api.core.item.FertilizerConfig; import net.momirealms.customcrops.api.core.item.FertilizerType; import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.misc.water.WateringMethod; -import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.util.PluginUtils; import net.momirealms.customcrops.common.config.ConfigLoader; import net.momirealms.customcrops.common.plugin.feature.Reloadable; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java index c5cfa9e9b..875692e95 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java @@ -17,10 +17,10 @@ package net.momirealms.customcrops.api.core; -import net.momirealms.customcrops.common.util.Key; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; import net.momirealms.customcrops.api.core.item.CustomCropsItem; import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.common.util.Key; public interface RegistryAccess { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java index 3a80d8d01..b3a94e4e6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java @@ -17,11 +17,11 @@ package net.momirealms.customcrops.api.core; -import net.momirealms.customcrops.common.util.Key; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; import net.momirealms.customcrops.api.core.item.CustomCropsItem; import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.common.util.Key; public class SimpleRegistryAccess implements RegistryAccess { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java index 21813ff46..0f8ad3aa6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java @@ -21,13 +21,13 @@ import com.flowpowered.nbt.IntTag; import com.flowpowered.nbt.StringTag; import com.flowpowered.nbt.Tag; -import net.momirealms.customcrops.common.util.Key; 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.core.wrapper.WrappedInteractEvent; import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; +import net.momirealms.customcrops.common.util.Key; public abstract class AbstractCustomCropsBlock implements CustomCropsBlock { 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 4b74f9a21..34447a88b 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 @@ -99,6 +99,7 @@ public void onBreak(WrappedBreakEvent event) { final Player player = event.playerBreaker(); Context context = Context.player(player); + context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(event.location())); // check requirements if (!RequirementManager.isSatisfied(context, cropConfig.breakRequirements())) { @@ -156,6 +157,7 @@ public void onPlace(WrappedPlaceEvent event) { public void onInteract(WrappedInteractEvent event) { final Player player = event.player(); Context context = Context.player(player); + context.arg(ContextKeys.SLOT, event.hand()); // data first CustomCropsWorld world = event.world(); @@ -171,7 +173,8 @@ public void onInteract(WrappedInteractEvent event) { } int point = point(state); - CropStageConfig stageConfig = cropConfig.getFloorStageEntry(point).getValue(); + CropStageConfig stageConfig = cropConfig.stageByID(event.relatedID()); + assert stageConfig != null; if (!RequirementManager.isSatisfied(context, stageConfig.interactRequirements())) { return; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java index c40d08826..d0a41669c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java @@ -18,13 +18,13 @@ package net.momirealms.customcrops.api.core.block; import com.flowpowered.nbt.CompoundMap; -import net.momirealms.customcrops.common.util.Key; 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.core.wrapper.WrappedInteractEvent; import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; +import net.momirealms.customcrops.common.util.Key; public interface CustomCropsBlock { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 82672f247..a7dbfd708 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -25,7 +25,6 @@ import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.item.Fertilizer; import net.momirealms.customcrops.api.core.item.FertilizerConfig; -import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; @@ -33,6 +32,7 @@ import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; import net.momirealms.customcrops.api.event.*; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.requirement.RequirementManager; import net.momirealms.customcrops.api.util.EventUtils; import net.momirealms.customcrops.api.util.LocationUtils; @@ -222,6 +222,7 @@ public void onInteract(WrappedInteractEvent event) { final Player player = event.player(); Context context = Context.player(player); + context.arg(ContextKeys.SLOT, event.hand()); context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); // check use requirements diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java index 5de6fdddc..496491fca 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java @@ -19,9 +19,9 @@ import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.item.FertilizerType; -import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.WaterBar; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.requirement.Requirement; import net.momirealms.customcrops.common.util.Pair; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java index 05cb6bae6..68eb34817 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java @@ -20,9 +20,9 @@ import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; import net.momirealms.customcrops.api.core.item.FertilizerType; -import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.WaterBar; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.requirement.Requirement; import net.momirealms.customcrops.common.util.Pair; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java index 5ac78b97d..d5cb1402b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java @@ -26,7 +26,9 @@ import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent; import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent; -import net.momirealms.customcrops.api.event.*; +import net.momirealms.customcrops.api.event.ScarecrowBreakEvent; +import net.momirealms.customcrops.api.event.ScarecrowInteractEvent; +import net.momirealms.customcrops.api.event.ScarecrowPlaceEvent; import net.momirealms.customcrops.api.util.EventUtils; import java.util.Optional; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java index 850b816ee..c340fd183 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -18,11 +18,12 @@ package net.momirealms.customcrops.api.core.block; import com.flowpowered.nbt.IntTag; +import com.flowpowered.nbt.Tag; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; @@ -33,8 +34,10 @@ import net.momirealms.customcrops.api.event.SprinklerFillEvent; import net.momirealms.customcrops.api.event.SprinklerInteractEvent; import net.momirealms.customcrops.api.event.SprinklerPlaceEvent; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.requirement.RequirementManager; import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.PlayerUtils; import org.bukkit.GameMode; import org.bukkit.Location; @@ -77,6 +80,7 @@ public void onBreak(WrappedBreakEvent event) { final Player player = event.playerBreaker(); Context context = Context.player(player); + context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(event.location())); CustomCropsBlockState state = fixOrGetState(world, pos3, config, event.brokenID()); if (!RequirementManager.isSatisfied(context, config.breakRequirements())) { event.setCancelled(true); @@ -103,6 +107,7 @@ public void onPlace(WrappedPlaceEvent event) { final Player player = event.player(); Context context = Context.player(player); + context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(event.location())); if (!RequirementManager.isSatisfied(context, config.placeRequirements())) { event.setCancelled(true); return; @@ -140,8 +145,11 @@ public void onInteract(WrappedInteractEvent event) { } final Player player = event.player(); + Location location = LocationUtils.toBlockLocation(event.location()); Context context = Context.player(player); - CustomCropsBlockState state = fixOrGetState(event.world(), Pos3.from(event.location()), config, event.relatedID()); + context.arg(ContextKeys.SLOT, event.hand()); + context.arg(ContextKeys.LOCATION, location); + CustomCropsBlockState state = fixOrGetState(event.world(), Pos3.from(location), config, event.relatedID()); if (!RequirementManager.isSatisfied(context, config.useRequirements())) { return; } @@ -149,14 +157,20 @@ public void onInteract(WrappedInteractEvent event) { int waterInSprinkler = water(state); String itemID = event.itemID(); ItemStack itemInHand = event.itemInHand(); + + context.arg(ContextKeys.STORAGE, config.storage()); + if (!config.infinite()) { for (WateringMethod method : config.wateringMethods()) { if (method.getUsed().equals(itemID) && method.getUsedAmount() <= itemInHand.getAmount()) { if (method.checkRequirements(context)) { if (waterInSprinkler >= config.storage()) { + context.arg(ContextKeys.CURRENT_WATER, waterInSprinkler); + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(config.waterBar()).map(it -> it.getWaterBar(waterInSprinkler, config.storage())).orElse("")); ActionManager.trigger(context, config.fullWaterActions()); + ActionManager.trigger(context, config.interactActions()); } else { - SprinklerFillEvent waterEvent = new SprinklerFillEvent(player, itemInHand, event.hand(), event.location(), method, state, config); + SprinklerFillEvent waterEvent = new SprinklerFillEvent(player, itemInHand, event.hand(), location, method, state, config); if (EventUtils.fireAndCheckCancel(waterEvent)) return; if (player.getGameMode() != GameMode.CREATIVE) { @@ -168,8 +182,16 @@ public void onInteract(WrappedInteractEvent event) { } } } + int currentWater = Math.min(config.storage(), waterInSprinkler + method.amountOfWater()); + context.arg(ContextKeys.CURRENT_WATER, currentWater); + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(config.waterBar()).map(it -> it.getWaterBar(currentWater, config.storage())).orElse("")); + if (addWater(state, config, method.amountOfWater()) && !config.threeDItem().equals(config.threeDItemWithWater())) { + updateBlockAppearance(location, config, false); + } + method.triggerActions(context); ActionManager.trigger(context, config.addWaterActions()); + ActionManager.trigger(context, config.interactActions()); } } return; @@ -177,7 +199,10 @@ public void onInteract(WrappedInteractEvent event) { } } - SprinklerInteractEvent interactEvent = new SprinklerInteractEvent(player, event.itemInHand(), event.location(), config, state, event.hand()); + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(config.waterBar()).map(it -> it.getWaterBar(waterInSprinkler, config.storage())).orElse("")); + context.arg(ContextKeys.CURRENT_WATER, waterInSprinkler); + + SprinklerInteractEvent interactEvent = new SprinklerInteractEvent(player, itemInHand, location, config, state, event.hand()); if (EventUtils.fireAndCheckCancel(interactEvent)) { return; } @@ -220,8 +245,7 @@ private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld worl if (water <= 0) { return; } - water(state, --water); - updateState = water == 0; + updateState = water(state, config, water - 1); } else { updateState = false; } @@ -229,6 +253,7 @@ private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld worl Context context = Context.block(state); World bukkitWorld = world.bukkitWorld(); Location bukkitLocation = location.toLocation(bukkitWorld); + context.arg(ContextKeys.LOCATION, bukkitLocation); CompletableFuture syncCheck = new CompletableFuture<>(); @@ -300,7 +325,11 @@ public boolean addWater(CustomCropsBlockState state, SprinklerConfig config, int } public int water(CustomCropsBlockState state) { - return state.get("water").getAsIntTag().map(IntTag::getValue).orElse(0); + Tag tag = state.get("water"); + if (tag == null) { + return 0; + } + return tag.getAsIntTag().map(IntTag::getValue).orElse(0); } public boolean water(CustomCropsBlockState state, int water) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java index 9b6c95e32..5f9b30fbb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java @@ -19,9 +19,9 @@ import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; -import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.WaterBar; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.requirement.Requirement; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java index 58472ec36..5fa046886 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java @@ -19,9 +19,9 @@ import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; -import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.WaterBar; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.requirement.Requirement; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java index 5913a3ddd..47a9e7270 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractCustomCropsItem.java @@ -17,10 +17,10 @@ package net.momirealms.customcrops.api.core.item; -import net.momirealms.customcrops.common.util.Key; import net.momirealms.customcrops.api.core.InteractionResult; import net.momirealms.customcrops.api.core.wrapper.WrappedInteractAirEvent; import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.common.util.Key; public abstract class AbstractCustomCropsItem implements CustomCropsItem { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java index 2b3198c85..bb2b993ed 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/CustomCropsItem.java @@ -17,10 +17,10 @@ package net.momirealms.customcrops.api.core.item; -import net.momirealms.customcrops.common.util.Key; import net.momirealms.customcrops.api.core.InteractionResult; import net.momirealms.customcrops.api.core.wrapper.WrappedInteractAirEvent; import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; +import net.momirealms.customcrops.common.util.Key; public interface CustomCropsItem { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java index 10505bdee..87993f327 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java @@ -20,6 +20,7 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; import net.momirealms.customcrops.api.core.BuiltInItemMechanics; import net.momirealms.customcrops.api.core.InteractionResult; @@ -35,6 +36,7 @@ import net.momirealms.customcrops.api.event.FertilizerUseEvent; import net.momirealms.customcrops.api.requirement.RequirementManager; import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -56,12 +58,14 @@ public InteractionResult interactAt(WrappedInteractEvent event) { return InteractionResult.COMPLETE; } + Location targetLocation = LocationUtils.toBlockLocation(event.location()); final Player player = event.player(); final Context context = Context.player(player); + context.arg(ContextKeys.SLOT, event.hand()); + final CustomCropsWorld world = event.world(); final ItemStack itemInHand = event.itemInHand(); String targetBlockID = event.relatedID(); - Location targetLocation = event.location(); // if the clicked block is a crop, correct the target block List cropConfigs = Registries.STAGE_TO_CROP_UNSAFE.get(event.relatedID()); @@ -71,6 +75,8 @@ public InteractionResult interactAt(WrappedInteractEvent event) { targetBlockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(targetLocation); } + context.arg(ContextKeys.LOCATION, targetLocation); + // if the clicked block is a pot PotConfig potConfig = Registries.ITEM_TO_POT.get(targetBlockID); if (potConfig != null) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java index 3ddb0f5f9..6aa8366de 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java @@ -64,7 +64,10 @@ public InteractionResult interactAt(WrappedInteractEvent event) { if (event.clickedBlockFace() != BlockFace.UP) return InteractionResult.PASS; final Player player = event.player(); + Location seedLocation = LocationUtils.toBlockLocation(event.location().add(0, 1, 0)); Context context = Context.player(player); + context.arg(ContextKeys.SLOT, event.hand()); + context.arg(ContextKeys.LOCATION, seedLocation); // check pot whitelist if (!cropConfig.potWhitelist().contains(potConfig.id())) { ActionManager.trigger(context, cropConfig.wrongPotActions()); @@ -74,7 +77,6 @@ public InteractionResult interactAt(WrappedInteractEvent event) { return InteractionResult.COMPLETE; } // check if the block is empty - Location seedLocation = event.location().add(0, 1, 0); if (!suitableForSeed(seedLocation)) { return InteractionResult.COMPLETE; } @@ -112,7 +114,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { if (player.getGameMode() != GameMode.CREATIVE) itemInHand.setAmount(itemInHand.getAmount() - 1); // place model - BukkitCustomCropsPlugin.getInstance().getItemManager().place(seedLocation, form, stageID, cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE); + BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(seedLocation), form, stageID, cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE); cropBlock.point(state, point); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java index 96ecae824..be564837d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java @@ -20,6 +20,7 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.block.SprinklerBlock; import net.momirealms.customcrops.api.core.block.SprinklerConfig; @@ -54,7 +55,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { // should be place on block if (event.existenceForm() != ExistenceForm.BLOCK) return InteractionResult.PASS; - SprinklerConfig config = Registries.SPRINKLER.get(event.itemID()); + SprinklerConfig config = Registries.ITEM_TO_SPRINKLER.get(event.itemID()); if (config == null) { return InteractionResult.COMPLETE; } @@ -68,7 +69,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { return InteractionResult.PASS; if (!clicked.isSolid()) return InteractionResult.PASS; - targetLocation = event.location().clone().add(0,1,0); + targetLocation = LocationUtils.toBlockLocation(event.location().clone().add(0,1,0)); if (!suitableForSprinkler(targetLocation)) { return InteractionResult.PASS; } @@ -77,6 +78,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { final Player player = event.player(); final ItemStack itemInHand = event.itemInHand(); Context context = Context.player(player); + context.arg(ContextKeys.LOCATION, targetLocation); // check requirements if (!RequirementManager.isSatisfied(context, config.placeRequirements())) { return InteractionResult.COMPLETE; @@ -100,11 +102,11 @@ public InteractionResult interactAt(WrappedInteractEvent event) { SprinklerPlaceEvent placeEvent = new SprinklerPlaceEvent(player, itemInHand, event.hand(), targetLocation.clone(), config, state); if (EventUtils.fireAndCheckCancel(placeEvent)) return InteractionResult.COMPLETE; + // clear replaceable block targetLocation.getBlock().setType(Material.AIR, false); if (player.getGameMode() != GameMode.CREATIVE) itemInHand.setAmount(itemInHand.getAmount() - 1); - // place the sprinkler BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(targetLocation), config.existenceForm(), config.threeDItem(), FurnitureRotation.NONE); world.addBlockState(pos3, state).ifPresent(previous -> { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java index ce67f54a4..875b9aabc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java @@ -18,9 +18,9 @@ package net.momirealms.customcrops.api.core.item; import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.misc.water.WaterBar; -import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.requirement.Requirement; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java index ca1d61cf3..06fc187aa 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java @@ -18,9 +18,9 @@ package net.momirealms.customcrops.api.core.item; import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.misc.water.WaterBar; -import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.requirement.Requirement; import org.bukkit.entity.Player; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java index 647a650cc..e06f87dd7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java @@ -25,7 +25,6 @@ import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.block.*; -import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; @@ -35,8 +34,10 @@ import net.momirealms.customcrops.api.event.WateringCanWaterPotEvent; import net.momirealms.customcrops.api.event.WateringCanWaterSprinklerEvent; import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.requirement.RequirementManager; import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.common.helper.AdventureHelper; import net.momirealms.customcrops.common.item.Item; import net.momirealms.customcrops.common.util.Pair; @@ -112,6 +113,7 @@ public void interactAir(WrappedInteractAirEvent event) { final Player player = event.player();; Context context = Context.player(player); + context.arg(ContextKeys.SLOT, event.hand()); // check requirements if (!RequirementManager.isSatisfied(context, config.requirements())) return; @@ -166,7 +168,10 @@ public InteractionResult interactAt(WrappedInteractEvent event) { return InteractionResult.COMPLETE; final Player player = event.player(); + Location targetLocation = LocationUtils.toBlockLocation(event.location()); final Context context = Context.player(player); + context.arg(ContextKeys.SLOT, event.hand()); + context.arg(ContextKeys.LOCATION, targetLocation); // check watering can requirements if (!RequirementManager.isSatisfied(context, wateringCanConfig.requirements())) { @@ -176,7 +181,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { final CustomCropsWorld world = event.world(); final ItemStack itemInHand = event.itemInHand(); String targetBlockID = event.relatedID(); - Location targetLocation = event.location(); + BlockFace blockFace = event.clickedBlockFace(); int waterInCan = getCurrentWater(itemInHand); diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java index 78379cef6..88dc70c5b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java @@ -18,8 +18,8 @@ package net.momirealms.customcrops.api.event; import net.momirealms.customcrops.api.core.block.PotConfig; -import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java index 08a64c118..d2d1d12fa 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java @@ -18,8 +18,8 @@ package net.momirealms.customcrops.api.event; import net.momirealms.customcrops.api.core.block.SprinklerConfig; -import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.misc.water.WateringMethod; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java b/api/src/main/java/net/momirealms/customcrops/api/misc/HologramManager.java similarity index 74% rename from plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java rename to api/src/main/java/net/momirealms/customcrops/api/misc/HologramManager.java index 04aa6f0b1..293499e34 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/misc/HologramManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/HologramManager.java @@ -15,17 +15,15 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.bukkit.misc; +package net.momirealms.customcrops.api.misc; -import net.kyori.adventure.text.Component; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; -import net.momirealms.customcrops.common.helper.AdventureHelper; import net.momirealms.customcrops.common.helper.VersionHelper; import net.momirealms.customcrops.common.plugin.feature.Reloadable; import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; import net.momirealms.customcrops.common.util.Pair; import net.momirealms.sparrow.heart.SparrowHeart; -import net.momirealms.sparrow.heart.feature.entity.FakeEntity; +import net.momirealms.sparrow.heart.feature.entity.FakeNamedEntity; import net.momirealms.sparrow.heart.feature.entity.armorstand.FakeArmorStand; import net.momirealms.sparrow.heart.feature.entity.display.FakeTextDisplay; import org.bukkit.Bukkit; @@ -96,24 +94,24 @@ public void onQuit(PlayerQuitEvent event) { this.hologramMap.remove(event.getPlayer().getUniqueId()); } - public void showHologram(Player player, Location location, Component component, int millis) { + public void showHologram(Player player, Location location, String json, int millis) { HologramCache hologramCache = hologramMap.get(player.getUniqueId()); if (hologramCache != null) { - hologramCache.showHologram(player, location, component, millis); + hologramCache.showHologram(player, location, json, millis); } else { hologramCache = new HologramCache(); - hologramCache.showHologram(player, location, component, millis); + hologramCache.showHologram(player, location, json, millis); hologramMap.put(player.getUniqueId(), hologramCache); } } public static class HologramCache { - private final ConcurrentHashMap> cache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> cache = new ConcurrentHashMap<>(); public void removeOutDated(long current, Player player) { ArrayList removed = new ArrayList<>(); - for (Map.Entry> entry : cache.entrySet()) { + for (Map.Entry> entry : cache.entrySet()) { if (entry.getValue().right() < current) { entry.getValue().left().destroy(player); removed.add(entry.getKey()); @@ -124,36 +122,23 @@ public void removeOutDated(long current, Player player) { } } - public void showHologram(Player player, Location location, Component component, int millis) { - Pair pair = cache.get(location); + public void showHologram(Player player, Location location, String json, int millis) { + Pair pair = cache.get(location); if (pair != null) { - pair.left().destroy(player); + pair.left().name(json); + pair.left().updateMetaData(player); pair.right(System.currentTimeMillis() + millis); - if (VersionHelper.isVersionNewerThan1_19_4()) { - FakeTextDisplay fakeEntity = SparrowHeart.getInstance().createFakeTextDisplay(location.clone().add(0,1.25,0)); - fakeEntity.name(AdventureHelper.componentToJson(component)); - fakeEntity.rgba(0, 0, 0, 0); - fakeEntity.spawn(player); - pair.left(fakeEntity); - } else { - FakeArmorStand fakeEntity = SparrowHeart.getInstance().createFakeArmorStand(location); - fakeEntity.name(AdventureHelper.componentToJson(component)); - fakeEntity.small(true); - fakeEntity.invisible(true); - fakeEntity.spawn(player); - pair.left(fakeEntity); - } } else { long removeTime = System.currentTimeMillis() + millis; if (VersionHelper.isVersionNewerThan1_19_4()) { FakeTextDisplay fakeEntity = SparrowHeart.getInstance().createFakeTextDisplay(location.clone().add(0,1.25,0)); - fakeEntity.name(AdventureHelper.componentToJson(component)); + fakeEntity.name(json); fakeEntity.rgba(0, 0, 0, 0); fakeEntity.spawn(player); this.cache.put(location, Pair.of(fakeEntity, removeTime)); } else { FakeArmorStand fakeEntity = SparrowHeart.getInstance().createFakeArmorStand(location); - fakeEntity.name(AdventureHelper.componentToJson(component)); + fakeEntity.name(json); fakeEntity.small(true); fakeEntity.invisible(true); fakeEntity.spawn(player); @@ -163,7 +148,7 @@ public void showHologram(Player player, Location location, Component component, } public void removeAll(Player player) { - for (Map.Entry> entry : this.cache.entrySet()) { + for (Map.Entry> entry : this.cache.entrySet()) { entry.getValue().left().destroy(player); } cache.clear(); diff --git a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java index 6dbaaf11a..88796593a 100644 --- a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java +++ b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java @@ -19,7 +19,6 @@ import com.flowpowered.nbt.*; import com.infernalsuite.aswm.api.world.SlimeWorld; -import net.momirealms.customcrops.common.util.Key; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.Registries; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; @@ -27,6 +26,7 @@ import net.momirealms.customcrops.api.core.world.adaptor.AbstractWorldAdaptor; import net.momirealms.customcrops.api.util.StringUtils; import net.momirealms.customcrops.common.helper.GsonHelper; +import net.momirealms.customcrops.common.util.Key; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Nullable; diff --git a/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java index 7b611cd17..64024ae36 100644 --- a/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java +++ b/compatibility-itemsadder-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/custom/itemsadder_r1/ItemsAdderListener.java @@ -21,6 +21,7 @@ import net.momirealms.customcrops.api.core.AbstractCustomEventListener; import net.momirealms.customcrops.api.core.AbstractItemManager; import net.momirealms.customcrops.api.util.DummyCancellable; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.inventory.EquipmentSlot; @@ -84,8 +85,11 @@ public void onPlaceCustomBlock(CustomBlockPlaceEvent event) { @EventHandler(ignoreCancelled = true) public void onPlaceFurniture(FurniturePlaceSuccessEvent event) { + Player player = event.getPlayer(); + // ItemsAdder would trigger FurniturePlaceSuccessEvent if the furniture is placed by API + if (player == null) return; itemManager.handlePlayerPlace( - event.getPlayer(), + player, event.getBukkitEntity().getLocation(), event.getNamespacedID(), EquipmentSlot.HAND, diff --git a/gradle.properties b/gradle.properties index 9009dbc62..63f69e98f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,8 +14,8 @@ asm_version=9.7 asm_commons_version=9.7 jar_relocator_version=1.7 adventure_bundle_version=4.17.0 -adventure_platform_version=4.3.3 -sparrow_heart_version=0.38 +adventure_platform_version=4.3.4 +sparrow_heart_version=0.39 cloud_core_version=2.0.0-rc.2 cloud_services_version=2.0.0-rc.2 cloud_brigadier_version=2.0.0-beta.9 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 3882e21ff..128eecfcb 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -24,6 +24,7 @@ import net.momirealms.customcrops.api.core.item.*; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; +import net.momirealms.customcrops.api.misc.HologramManager; import net.momirealms.customcrops.api.misc.cooldown.CoolDownManager; import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; import net.momirealms.customcrops.api.util.EventUtils; @@ -33,7 +34,6 @@ import net.momirealms.customcrops.bukkit.config.BukkitConfigManager; import net.momirealms.customcrops.bukkit.integration.BukkitIntegrationManager; import net.momirealms.customcrops.bukkit.item.BukkitItemManager; -import net.momirealms.customcrops.bukkit.misc.HologramManager; import net.momirealms.customcrops.bukkit.requirement.BlockRequirementManager; import net.momirealms.customcrops.bukkit.requirement.PlayerRequirementManager; import net.momirealms.customcrops.bukkit.scheduler.BukkitSchedulerAdapter; @@ -166,8 +166,9 @@ public void enable() { boolean downloadFromBBB = buildByBit.equals("true"); this.getScheduler().sync().runLater(() -> { - this.reload(); + getPluginLogger().info("CustomCrops Registry has been frozen"); ((SimpleRegistryAccess) registryAccess).freeze(); + this.reload(); if (ConfigManager.metrics()) new Metrics((JavaPlugin) getBoostrap(), 16593); if (ConfigManager.checkUpdate()) { VersionHelper.UPDATE_CHECKER.apply(this).thenAccept(result -> { diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java index 408d23de8..84a7d8dc7 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java @@ -26,19 +26,16 @@ import net.momirealms.customcrops.api.action.AbstractActionManager; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.context.ContextKeys; -import net.momirealms.customcrops.api.core.block.CropBlock; -import net.momirealms.customcrops.api.core.block.CropConfig; -import net.momirealms.customcrops.api.core.block.CropStageConfig; -import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.block.SprinklerBlock; +import net.momirealms.customcrops.api.core.block.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; import net.momirealms.customcrops.api.misc.value.MathValue; import net.momirealms.customcrops.api.misc.value.TextValue; -import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.api.util.PlayerUtils; import net.momirealms.customcrops.bukkit.integration.VaultHook; -import net.momirealms.customcrops.bukkit.misc.HologramManager; import net.momirealms.customcrops.common.helper.AdventureHelper; import net.momirealms.customcrops.common.util.ListUtils; import net.momirealms.customcrops.common.util.RandomUtils; @@ -53,7 +50,10 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import java.util.*; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; import static java.util.Objects.requireNonNull; @@ -85,7 +85,6 @@ protected void registerBuiltInActions() { this.registerTitleAction(); this.registerSwingHandAction(); this.registerForceTickAction(); - this.registerHologramAction(); this.registerMessageAction(); } @@ -447,80 +446,19 @@ private void registerTitleAction() { }, "random-title"); } - private void registerHologramAction() { - registerAction(((args, chance) -> { - if (args instanceof Section section) { - TextValue text = TextValue.auto(section.getString("text", "")); - MathValue duration = MathValue.auto(section.get("duration", 20)); - boolean position = section.getString("position", "other").equals("other"); - MathValue x = MathValue.auto(section.get("x", 0)); - MathValue y = MathValue.auto(section.get("y", 0)); - MathValue z = MathValue.auto(section.get("z", 0)); - boolean applyCorrection = section.getBoolean("apply-correction", false); - boolean onlyShowToOne = !section.getBoolean("visible-to-all", false); - int range = section.getInt("range", 32); - return context -> { - if (context.holder() == null) return; - if (Math.random() > chance) return; - Player owner = context.holder(); - Location location = position ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : owner.getLocation().clone(); - location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); - Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); - if (optionalWorld.isEmpty()) { - return; - } - Pos3 pos3 = Pos3.from(location); - if (applyCorrection) { - Optional optionalState = optionalWorld.get().getBlockState(pos3); - if (optionalState.isPresent()) { - if (optionalState.get().type() instanceof CropBlock cropBlock) { - CropConfig config = cropBlock.config(optionalState.get()); - int point = cropBlock.point(optionalState.get()); - if (config != null) { - int tempPoints = point; - while (tempPoints >= 0) { - Map.Entry entry = config.getFloorStageEntry(tempPoints); - CropStageConfig stage = entry.getValue(); - if (stage.stageID() != null) { - location.add(0, stage.displayInfoOffset(), 0); - break; - } - tempPoints = stage.point() - 1; - } - } - } - } - } - ArrayList viewers = new ArrayList<>(); - if (onlyShowToOne) { - if (owner == null) return; - viewers.add(owner); - } else { - for (Player player : owner.getWorld().getPlayers()) { - if (LocationUtils.getDistance(player.getLocation(), location) <= range) { - viewers.add(player); - } - } - } - Component component = AdventureHelper.miniMessage(text.render(context)); - for (Player viewer : viewers) { - HologramManager.getInstance().showHologram(viewer, location, component, (int) (duration.evaluate(context) * 50)); - } - }; - } else { - plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at hologram action which is expected to be `Section`"); - return Action.empty(); - } - }), "hologram"); - } - private void registerSwingHandAction() { registerAction((args, chance) -> { boolean arg = (boolean) args; return context -> { if (context.holder() == null) return; if (Math.random() > chance) return; - SparrowHeart.getInstance().swingHand(context.holder(), arg ? HandSlot.MAIN : HandSlot.OFF); + EquipmentSlot slot = context.arg(ContextKeys.SLOT); + if (slot == null) { + SparrowHeart.getInstance().swingHand(context.holder(), arg ? HandSlot.MAIN : HandSlot.OFF); + } else { + if (slot == EquipmentSlot.HAND) SparrowHeart.getInstance().swingHand(context.holder(), HandSlot.MAIN); + if (slot == EquipmentSlot.OFF_HAND) SparrowHeart.getInstance().swingHand(context.holder(), HandSlot.OFF); + } }; }, "swing-hand"); } @@ -533,9 +471,28 @@ private void registerForceTickAction() { Pos3 pos3 = Pos3.from(location); Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> { - state.type().randomTick(state, world, pos3); - state.type().scheduledTick(state, world, pos3); + CustomCropsBlock customCropsBlock = state.type(); + customCropsBlock.randomTick(state, world, pos3); + customCropsBlock.scheduledTick(state, world, pos3); + if (customCropsBlock instanceof SprinklerBlock sprinklerBlock) { + int water = sprinklerBlock.water(state); + SprinklerConfig config = sprinklerBlock.config(state); + context.arg(ContextKeys.CURRENT_WATER, water); + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(config.waterBar()).map(it -> it.getWaterBar(water, config.storage())).orElse("")); + } })); }, "force-tick"); + registerAction((args, chance) -> context -> { + if (context.holder() == null) return; + if (Math.random() > chance) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Pos3 pos3 = Pos3.from(location); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> { + CustomCropsBlock customCropsBlock = state.type(); + customCropsBlock.randomTick(state, world, pos3); + customCropsBlock.scheduledTick(state, world, pos3); + })); + }, "tick"); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java index 0049ef56e..b5ca93c69 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -38,15 +38,14 @@ import net.momirealms.customcrops.api.core.item.WateringCanConfig; import net.momirealms.customcrops.api.util.PluginUtils; import net.momirealms.customcrops.common.helper.AdventureHelper; -import net.momirealms.customcrops.common.helper.VersionHelper; import net.momirealms.customcrops.common.locale.TranslationManager; import net.momirealms.customcrops.common.plugin.CustomCropsProperties; import net.momirealms.customcrops.common.util.ListUtils; -import org.bukkit.Bukkit; -import org.bukkit.Particle; -import java.io.*; -import java.nio.charset.StandardCharsets; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.*; public class BukkitConfigManager extends ConfigManager { diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java index 3ebe695ff..6c8951ef9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -33,8 +33,8 @@ import net.momirealms.customcrops.api.core.item.FertilizerType; import net.momirealms.customcrops.api.core.item.WateringCanConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; -import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.requirement.RequirementManager; import net.momirealms.customcrops.common.util.Pair; import net.momirealms.customcrops.common.util.TriFunction; 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 e65844826..2c4f86687 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 @@ -197,6 +197,7 @@ public void placeBlock(@NotNull Location location, @NotNull String id) { @Override public void placeFurniture(@NotNull Location location, @NotNull String id, FurnitureRotation rotation) { Entity entity = this.provider.placeFurniture(location, id); + if (rotation == FurnitureRotation.NONE) return; if (entity != null) { if (entity instanceof ItemFrame itemFrame) { itemFrame.setRotation(rotation.getBukkitRotation()); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java index fa16d8648..d2066ce55 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/requirement/PlayerRequirementManager.java @@ -20,6 +20,7 @@ import dev.dejvokep.boostedyaml.block.implementation.Section; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.integration.LevelerProvider; import net.momirealms.customcrops.api.misc.value.MathValue; import net.momirealms.customcrops.api.requirement.AbstractRequirementManager; @@ -27,6 +28,7 @@ import net.momirealms.customcrops.bukkit.integration.VaultHook; import net.momirealms.customcrops.common.util.ListUtils; import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; @@ -63,13 +65,18 @@ private void registerItemInHandRequirement() { registerRequirement((args, actions, runActions) -> { if (args instanceof Section section) { boolean mainOrOff = section.getString("hand","main").equalsIgnoreCase("main"); - int amount = section.getInt("amount", 1); + int amount = section.getInt("amount", 0); List items = ListUtils.toList(section.get("item")); return context -> { - if (context.holder() == null) return true; - ItemStack itemStack = mainOrOff ? - context.holder().getInventory().getItemInMainHand() - : context.holder().getInventory().getItemInOffHand(); + Player player = context.holder(); + if (player == null) return true; + EquipmentSlot slot = context.arg(ContextKeys.SLOT); + ItemStack itemStack; + if (slot == null) { + itemStack = mainOrOff ? player.getInventory().getItemInMainHand() : player.getInventory().getItemInOffHand(); + } else { + itemStack = player.getInventory().getItem(slot); + } String id = plugin.getItemManager().id(itemStack); if (items.contains(id) && itemStack.getAmount() >= amount) return true; if (runActions) ActionManager.trigger(context, actions); From 11a6e803d48f581cea7cbd49791f4c706d35c2f1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:04:04 +0800 Subject: [PATCH 069/329] implement fertilizers --- .../api/core/SynchronizedCompoundMap.java | 25 ++++++++++++++----- .../customcrops/api/core/block/PotBlock.java | 8 +++--- .../customcrops/api/core/block/PotConfig.java | 4 +++ .../api/core/block/PotConfigImpl.java | 19 ++++++++++++-- .../api/core/item/FertilizerItem.java | 3 ++- .../customcrops/bukkit/config/ConfigType.java | 1 + .../main/resources/contents/pots/default.yml | 2 ++ 7 files changed, 49 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java index e0a3dbf3b..c463d48b3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java @@ -19,7 +19,9 @@ import com.flowpowered.nbt.CompoundMap; import com.flowpowered.nbt.Tag; +import com.flowpowered.nbt.TagType; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.StringJoiner; @@ -78,25 +80,36 @@ public boolean equals(Object o) { @Override public String toString() { - return compoundMapToString(compoundMap); + return compoundMapToString("BlockData", compoundMap); } - private String compoundMapToString(CompoundMap compoundMap) { + private String compoundMapToString(String key, CompoundMap compoundMap) { StringJoiner joiner = new StringJoiner(", "); for (Map.Entry> entry : compoundMap.entrySet()) { Tag tag = entry.getValue(); String tagValue; switch (tag.getType()) { - case TAG_STRING, TAG_BYTE, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_INT_ARRAY, TAG_LONG, TAG_SHORT, TAG_SHORT_ARRAY, TAG_LONG_ARRAY, TAG_BYTE_ARRAY, - TAG_LIST -> + case TAG_STRING, TAG_BYTE, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_INT_ARRAY, TAG_LONG, TAG_SHORT, TAG_SHORT_ARRAY, TAG_LONG_ARRAY, TAG_BYTE_ARRAY -> tagValue = tag.getValue().toString(); - case TAG_COMPOUND -> tagValue = compoundMapToString((CompoundMap) tag.getValue()); + case TAG_LIST -> { + List> list = (List>) tag.getValue(); + StringJoiner listJoiner = new StringJoiner(", "); + for (Tag tag2 : list) { + if (tag2.getType() == TagType.TAG_COMPOUND) { + listJoiner.add(compoundMapToString(tag2.getName(), (CompoundMap) tag2.getValue())); + } else { + listJoiner.add(tag2.getValue().toString()); + } + } + tagValue = tag.getName() + "[" + listJoiner + "]"; + } + case TAG_COMPOUND -> tagValue = compoundMapToString(tag.getName(), (CompoundMap) tag.getValue()); default -> { continue; } } joiner.add("\"" + entry.getKey() + "\":\"" + tagValue + "\""); } - return "BlockData{" + joiner + "}"; + return key + "{" + joiner + "}"; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index a7dbfd708..e29d8eb83 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -437,7 +437,7 @@ public PotConfig config(CustomCropsBlockState state) { public Fertilizer[] fertilizers(CustomCropsBlockState state) { Tag fertilizerTag = state.get("fertilizers"); if (fertilizerTag == null) return new Fertilizer[0]; - List tags = ((ListTag) fertilizerTag.getValue()).getValue(); + List tags = ((List) fertilizerTag.getValue()); Fertilizer[] fertilizers = new Fertilizer[tags.size()]; for (int i = 0; i < tags.size(); i++) { CompoundTag tag = tags.get(i); @@ -480,10 +480,10 @@ public boolean canApplyFertilizer(CustomCropsBlockState state, Fertilizer fertil public boolean addFertilizer(CustomCropsBlockState state, Fertilizer fertilizer) { Tag fertilizerTag = state.get("fertilizers"); if (fertilizerTag == null) { - fertilizerTag = new ListTag("", TagType.TAG_COMPOUND, new ArrayList<>()); + fertilizerTag = new ListTag("fertilizers", TagType.TAG_COMPOUND, new ArrayList<>()); state.set("fertilizers", fertilizerTag); } - List tags = ((ListTag) fertilizerTag.getValue()).getValue(); + List tags = ((List) fertilizerTag.getValue()); for (CompoundTag tag : tags) { CompoundMap map = tag.getValue(); Fertilizer applied = tagToFertilizer(map); @@ -510,7 +510,7 @@ private boolean tickFertilizer(CustomCropsBlockState state) { if (fertilizerTag == null) { return false; } - List tags = ((ListTag) fertilizerTag.getValue()).getValue(); + List tags = ((List) fertilizerTag.getValue()); if (tags.isEmpty()) { return false; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java index 496491fca..cc804f526 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java @@ -71,6 +71,8 @@ public interface PotConfig { Action[] fullWaterActions(); + Action[] maxFertilizerActions(); + static Builder builder() { return new PotConfigImpl.BuilderImpl(); } @@ -113,6 +115,8 @@ interface Builder { Builder fullWaterActions(Action[] fullWaterActions); + Builder maxFertilizerActions(Action[] maxFertilizerActions); + Builder basicAppearance(Pair basicAppearance); Builder potAppearanceMap(HashMap> potAppearanceMap); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java index 68eb34817..b203610d3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java @@ -54,6 +54,7 @@ public class PotConfigImpl implements PotConfig { private final Action[] breakActions; private final Action[] addWaterActions; private final Action[] fullWaterActions; + private final Action[] maxFertilizerActions; public PotConfigImpl( String id, @@ -74,7 +75,8 @@ public PotConfigImpl( Action[] placeActions, Action[] breakActions, Action[] addWaterActions, - Action[] fullWaterActions + Action[] fullWaterActions, + Action[] maxFertilizerActions ) { this.id = id; this.basicAppearance = basicAppearance; @@ -95,6 +97,7 @@ public PotConfigImpl( this.breakActions = breakActions; this.addWaterActions = addWaterActions; this.fullWaterActions = fullWaterActions; + this.maxFertilizerActions = maxFertilizerActions; this.blocks.add(basicAppearance.left()); this.blocks.add(basicAppearance.right()); this.wetBlocks.add(basicAppearance.right()); @@ -211,6 +214,11 @@ public Action[] fullWaterActions() { return fullWaterActions; } + @Override + public Action[] maxFertilizerActions() { + return maxFertilizerActions; + } + public static class BuilderImpl implements Builder { private String id; @@ -233,10 +241,11 @@ public static class BuilderImpl implements Builder { private Action[] breakActions; private Action[] addWaterActions; private Action[] fullWaterActions; + private Action[] maxFertilizerActions; @Override public PotConfig build() { - return new PotConfigImpl(id, basicAppearance, potAppearanceMap, storage, isRainDropAccepted, isNearbyWaterAccepted, wateringMethods, waterBar, maxFertilizers, placeRequirements, breakRequirements, useRequirements, tickActions, reachLimitActions, interactActions, placeActions, breakActions, addWaterActions, fullWaterActions); + return new PotConfigImpl(id, basicAppearance, potAppearanceMap, storage, isRainDropAccepted, isNearbyWaterAccepted, wateringMethods, waterBar, maxFertilizers, placeRequirements, breakRequirements, useRequirements, tickActions, reachLimitActions, interactActions, placeActions, breakActions, addWaterActions, fullWaterActions, maxFertilizerActions); } @Override @@ -341,6 +350,12 @@ public Builder fullWaterActions(Action[] fullWaterActions) { return this; } + @Override + public Builder maxFertilizerActions(Action[] maxFertilizerActions) { + this.maxFertilizerActions = maxFertilizerActions; + return this; + } + @Override public Builder basicAppearance(Pair basicAppearance) { this.basicAppearance = basicAppearance; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java index 87993f327..d50f8f88a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java @@ -53,7 +53,7 @@ public FertilizerItem() { @Override public InteractionResult interactAt(WrappedInteractEvent event) { - FertilizerConfig fertilizerConfig = Registries.FERTILIZER.get(event.itemID()); + FertilizerConfig fertilizerConfig = Registries.ITEM_TO_FERTILIZER.get(event.itemID()); if (fertilizerConfig == null) { return InteractionResult.COMPLETE; } @@ -114,6 +114,7 @@ public InteractionResult interactAt(WrappedInteractEvent event) { .build(); CustomCropsBlockState potState = potBlock.fixOrGetState(world, Pos3.from(targetLocation), potConfig, event.relatedID()); if (!potBlock.canApplyFertilizer(potState,fertilizer)) { + ActionManager.trigger(context, potConfig.maxFertilizerActions()); return InteractionResult.COMPLETE; } // trigger event diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java index 6c8951ef9..df56e6dee 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -125,6 +125,7 @@ public class ConfigType { .addWaterActions(pam.parseActions(section.getSection("events.add_water"))) .placeActions(pam.parseActions(section.getSection("events.place"))) .breakActions(pam.parseActions(section.getSection("events.break"))) + .maxFertilizerActions(pam.parseActions(section.getSection("events.max_fertilizers"))) .interactActions(pam.parseActions(section.getSection("events.interact"))) .reachLimitActions(pam.parseActions(section.getSection("events.reach_limit"))) .fullWaterActions(pam.parseActions(section.getSection("events.full"))) diff --git a/plugin/src/main/resources/contents/pots/default.yml b/plugin/src/main/resources/contents/pots/default.yml index 8b95198d5..0e2136d5d 100644 --- a/plugin/src/main/resources/contents/pots/default.yml +++ b/plugin/src/main/resources/contents/pots/default.yml @@ -11,6 +11,8 @@ default: absorb-rainwater: true # Does nearby water make the pot wet absorb-nearby-water: false + # The max amount of fertilizers + max-fertilizers: 1 # Set unique looks for pots with different fertilizer statuses fertilized-pots: quality: From ab6d8527ec56461bf1194a6a05bef79a4cda4b31 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:47:38 +0800 Subject: [PATCH 070/329] date season commands --- .../api/core/SynchronizedCompoundMap.java | 2 +- .../api/core/world/CustomCropsWorldImpl.java | 4 +- .../api/core/world/WorldManager.java | 3 + .../common/locale/MessageConstants.java | 16 ++- .../bukkit/command/BukkitCommandManager.java | 9 +- .../command/feature/GetDateCommand.java | 63 +++++++++++ .../command/feature/GetSeasonCommand.java | 57 ++++++++++ .../command/feature/SetDateCommand.java | 93 ++++++++++++++++ .../command/feature/SetSeasonCommand.java | 105 ++++++++++++++++++ .../bukkit/world/BukkitWorldManager.java | 12 +- plugin/src/main/resources/commands.yml | 14 +++ plugin/src/main/resources/translations/en.yml | 15 +++ .../src/main/resources/translations/zh_cn.yml | 16 ++- 13 files changed, 401 insertions(+), 8 deletions(-) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetDateCommand.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetSeasonCommand.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetDateCommand.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetSeasonCommand.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java index c463d48b3..48af31c19 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java @@ -101,7 +101,7 @@ private String compoundMapToString(String key, CompoundMap compoundMap) { listJoiner.add(tag2.getValue().toString()); } } - tagValue = tag.getName() + "[" + listJoiner + "]"; + tagValue = "[" + listJoiner + "]"; } case TAG_COMPOUND -> tagValue = compoundMapToString(tag.getName(), (CompoundMap) tag.getValue()); default -> { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java index cd92342d9..831144cab 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java @@ -203,7 +203,9 @@ private void timer() { saveLazyChunks(); saveLazyRegions(); if (isANewDay()) { - updateSeasonAndDate(); + if (setting().autoSeasonChange()) { + updateSeasonAndDate(); + } } if (setting().enableScheduler()) { tickChunks(); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java index 6dcaef677..17b9a33cd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.core.world; import net.momirealms.customcrops.api.core.world.adaptor.WorldAdaptor; +import net.momirealms.customcrops.api.integration.SeasonProvider; import net.momirealms.customcrops.common.plugin.feature.Reloadable; import org.bukkit.World; @@ -26,6 +27,8 @@ public interface WorldManager extends Reloadable { + SeasonProvider seasonProvider(); + Season getSeason(World world); int getDate(World world); diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java b/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java index 7305293d3..4fec42cca 100644 --- a/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java @@ -28,5 +28,19 @@ public interface MessageConstants { TranslatableComponent.Builder SEASON_AUTUMN = Component.translatable().key("season.autumn"); TranslatableComponent.Builder SEASON_WINTER = Component.translatable().key("season.winter"); TranslatableComponent.Builder SEASON_DISABLE = Component.translatable().key("season.disable"); - + TranslatableComponent.Builder COMMAND_GET_SEASON_SUCCESS = Component.translatable().key("command.season.get.success"); + TranslatableComponent.Builder COMMAND_GET_SEASON_FAILURE = Component.translatable().key("command.season.get.failure"); + TranslatableComponent.Builder COMMAND_SET_SEASON_SUCCESS = Component.translatable().key("command.season.set.success"); + TranslatableComponent.Builder COMMAND_SET_SEASON_FAILURE_DISABLE = Component.translatable().key("command.season.set.failure.disable"); + TranslatableComponent.Builder COMMAND_SET_SEASON_FAILURE_REFERENCE = Component.translatable().key("command.season.set.failure.reference"); + TranslatableComponent.Builder COMMAND_SET_SEASON_FAILURE_OTHER = Component.translatable().key("command.season.set.failure.other"); + TranslatableComponent.Builder COMMAND_SET_SEASON_FAILURE_INVALID = Component.translatable().key("command.season.set.failure.invalid"); + TranslatableComponent.Builder COMMAND_GET_DATE_SUCCESS = Component.translatable().key("command.date.get.success"); + TranslatableComponent.Builder COMMAND_GET_DATE_FAILURE_DISABLE = Component.translatable().key("command.date.get.failure.disable"); + TranslatableComponent.Builder COMMAND_GET_DATE_FAILURE_OTHER = Component.translatable().key("command.date.get.failure.other"); + TranslatableComponent.Builder COMMAND_SET_DATE_SUCCESS = Component.translatable().key("command.date.set.success"); + TranslatableComponent.Builder COMMAND_SET_DATE_FAILURE_DISABLE = Component.translatable().key("command.date.set.failure.disable"); + TranslatableComponent.Builder COMMAND_SET_DATE_FAILURE_REFERENCE = Component.translatable().key("command.date.set.failure.reference"); + TranslatableComponent.Builder COMMAND_SET_DATE_FAILURE_OTHER = Component.translatable().key("command.date.set.failure.other"); + TranslatableComponent.Builder COMMAND_SET_DATE_FAILURE_INVALID = Component.translatable().key("command.date.set.failure.invalid"); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java index beed78ba1..c7ee24ec4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java @@ -19,8 +19,7 @@ import net.kyori.adventure.util.Index; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; -import net.momirealms.customcrops.bukkit.command.feature.DebugDataCommand; -import net.momirealms.customcrops.bukkit.command.feature.ReloadCommand; +import net.momirealms.customcrops.bukkit.command.feature.*; import net.momirealms.customcrops.common.command.AbstractCommandManager; import net.momirealms.customcrops.common.command.CommandFeature; import net.momirealms.customcrops.common.sender.Sender; @@ -37,7 +36,11 @@ public class BukkitCommandManager extends AbstractCommandManager private final List> FEATURES = List.of( new ReloadCommand(this), - new DebugDataCommand(this) + new DebugDataCommand(this), + new GetSeasonCommand(this), + new SetSeasonCommand(this), + new GetDateCommand(this), + new SetDateCommand(this) ); private final Index> INDEX = Index.create(CommandFeature::getFeatureID, FEATURES); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetDateCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetDateCommand.java new file mode 100644 index 000000000..f156d0260 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetDateCommand.java @@ -0,0 +1,63 @@ +/* + * 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.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.world.Season; +import net.momirealms.customcrops.api.integration.SeasonProvider; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.locale.MessageConstants; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.WorldParser; + +public class GetDateCommand extends BukkitCommandFeature { + + public GetDateCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("world", WorldParser.worldParser()) + .handler(context -> { + World world = context.get("world"); + SeasonProvider provider = BukkitCustomCropsPlugin.getInstance().getWorldManager().seasonProvider(); + if (provider.identifier().equals("CustomCrops")) { + int date = BukkitCustomCropsPlugin.getInstance().getWorldManager().getDate(world); + if (date != -1) { + handleFeedback(context, MessageConstants.COMMAND_GET_DATE_SUCCESS, Component.text(world.getName()), Component.text(date)); + } else { + handleFeedback(context, MessageConstants.COMMAND_GET_DATE_FAILURE_DISABLE, Component.text(world.getName())); + } + } else { + handleFeedback(context, MessageConstants.COMMAND_GET_DATE_FAILURE_OTHER, Component.text(world.getName()), Component.text(provider.identifier())); + } + }); + } + + @Override + public String getFeatureID() { + return "get_date"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetSeasonCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetSeasonCommand.java new file mode 100644 index 000000000..ff71e8f79 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetSeasonCommand.java @@ -0,0 +1,57 @@ +/* + * 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.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.world.Season; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.locale.MessageConstants; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.WorldParser; + +public class GetSeasonCommand extends BukkitCommandFeature { + + public GetSeasonCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("world", WorldParser.worldParser()) + .handler(context -> { + World world = context.get("world"); + Season season = BukkitCustomCropsPlugin.getInstance().getWorldManager().getSeason(world); + if (season != Season.DISABLE) { + handleFeedback(context, MessageConstants.COMMAND_GET_SEASON_SUCCESS, Component.text(world.getName()), Component.text(season.translation())); + } else { + handleFeedback(context, MessageConstants.COMMAND_GET_SEASON_FAILURE, Component.text(world.getName())); + } + }); + } + + @Override + public String getFeatureID() { + return "get_season"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetDateCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetDateCommand.java new file mode 100644 index 000000000..2acfa1060 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetDateCommand.java @@ -0,0 +1,93 @@ +/* + * 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.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Season; +import net.momirealms.customcrops.api.integration.SeasonProvider; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.locale.MessageConstants; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.WorldParser; +import org.incendo.cloud.parser.standard.IntegerParser; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Locale; +import java.util.Optional; + +public class SetDateCommand extends BukkitCommandFeature { + + public SetDateCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("world", WorldParser.worldParser()) + .required("date", IntegerParser.integerParser()) + .handler(context -> { + World world = context.get("world"); + SeasonProvider provider = BukkitCustomCropsPlugin.getInstance().getWorldManager().seasonProvider(); + if (provider.identifier().equals("CustomCrops")) { + Optional> optionalWorld = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(world); + if (optionalWorld.isPresent()) { + CustomCropsWorld customCropsWorld = optionalWorld.get(); + int date = context.get("date"); + if (date < 1 || date > customCropsWorld.setting().seasonDuration()) { + handleFeedback(context, MessageConstants.COMMAND_SET_DATE_FAILURE_INVALID, Component.text(world.getName()), Component.text(date)); + return; + } + if (customCropsWorld.setting().enableSeason()) { + if (ConfigManager.syncSeasons()) { + if (ConfigManager.referenceWorld().equals(world.getName())) { + customCropsWorld.extraData().setDate(date); + handleFeedback(context, MessageConstants.COMMAND_SET_DATE_SUCCESS, Component.text(world.getName()), Component.text(date)); + } else { + handleFeedback(context, MessageConstants.COMMAND_SET_DATE_FAILURE_REFERENCE, Component.text(world.getName())); + } + } else { + customCropsWorld.extraData().setDate(date); + handleFeedback(context, MessageConstants.COMMAND_SET_DATE_SUCCESS, Component.text(world.getName()), Component.text(date)); + } + } else { + handleFeedback(context, MessageConstants.COMMAND_SET_DATE_FAILURE_DISABLE, Component.text(world.getName())); + } + } else { + handleFeedback(context, MessageConstants.COMMAND_SET_DATE_FAILURE_DISABLE, Component.text(world.getName())); + } + } else { + handleFeedback(context, MessageConstants.COMMAND_SET_DATE_FAILURE_OTHER, Component.text(world.getName()), Component.text(provider.identifier())); + } + }); + } + + @Override + public String getFeatureID() { + return "set_date"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetSeasonCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetSeasonCommand.java new file mode 100644 index 000000000..c5bf431ec --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetSeasonCommand.java @@ -0,0 +1,105 @@ +/* + * 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.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Season; +import net.momirealms.customcrops.api.integration.SeasonProvider; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.locale.MessageConstants; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.WorldParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Locale; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +public class SetSeasonCommand extends BukkitCommandFeature { + + public SetSeasonCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("world", WorldParser.worldParser()) + .required("season", StringParser.stringComponent().suggestionProvider(SuggestionProvider.suggesting( + Suggestion.suggestion("spring"), Suggestion.suggestion("summer"),Suggestion.suggestion("autumn"),Suggestion.suggestion("winter") + ))) + .handler(context -> { + World world = context.get("world"); + SeasonProvider provider = BukkitCustomCropsPlugin.getInstance().getWorldManager().seasonProvider(); + + if (provider.identifier().equals("CustomCrops")) { + Optional> optionalWorld = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(world); + if (optionalWorld.isPresent()) { + CustomCropsWorld customCropsWorld = optionalWorld.get(); + String season = context.get("season"); + Season seasonEnum; + try { + seasonEnum = Season.valueOf(season.toUpperCase(Locale.ENGLISH)); + if (seasonEnum == Season.DISABLE) { + throw new IllegalArgumentException("Invalid season: " + season); + } + } catch (IllegalArgumentException e) { + handleFeedback(context, MessageConstants.COMMAND_SET_SEASON_FAILURE_INVALID, Component.text(world.getName()), Component.text(season)); + return; + } + if (customCropsWorld.setting().enableSeason()) { + if (ConfigManager.syncSeasons()) { + if (ConfigManager.referenceWorld().equals(world.getName())) { + customCropsWorld.extraData().setSeason(seasonEnum); + handleFeedback(context, MessageConstants.COMMAND_SET_SEASON_SUCCESS, Component.text(world.getName()), Component.text(seasonEnum.translation())); + } else { + handleFeedback(context, MessageConstants.COMMAND_SET_SEASON_FAILURE_REFERENCE, Component.text(world.getName())); + } + } else { + customCropsWorld.extraData().setSeason(seasonEnum); + handleFeedback(context, MessageConstants.COMMAND_SET_SEASON_SUCCESS, Component.text(world.getName()), Component.text(seasonEnum.translation())); + } + } else { + handleFeedback(context, MessageConstants.COMMAND_SET_SEASON_FAILURE_DISABLE, Component.text(world.getName())); + } + } else { + handleFeedback(context, MessageConstants.COMMAND_SET_SEASON_FAILURE_DISABLE, Component.text(world.getName())); + } + } else { + handleFeedback(context, MessageConstants.COMMAND_SET_SEASON_FAILURE_OTHER, Component.text(world.getName()), Component.text(provider.identifier())); + } + }); + } + + @Override + public String getFeatureID() { + return "set_season"; + } +} 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 e4bb997f5..02a5cbb38 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 @@ -66,7 +66,12 @@ public BukkitWorldManager(BukkitCustomCropsPlugin plugin) { @NotNull @Override public Season getSeason(@NotNull World world) { - return BukkitWorldManager.this.getWorld(world).map(w -> w.extraData().getSeason()).orElse(Season.DISABLE); + return BukkitWorldManager.this.getWorld(world).map(w -> { + if (!w.setting().enableSeason()) { + return Season.DISABLE; + } + return w.extraData().getSeason(); + }).orElse(Season.DISABLE); } @Override public String identifier() { @@ -79,6 +84,11 @@ public void seasonProvider(SeasonProvider seasonProvider) { this.seasonProvider = seasonProvider; } + @Override + public SeasonProvider seasonProvider() { + return seasonProvider; + } + @Override public Season getSeason(World world) { if (ConfigManager.syncSeasons()) { diff --git a/plugin/src/main/resources/commands.yml b/plugin/src/main/resources/commands.yml index 4c1eba9a8..c5d32aaf3 100644 --- a/plugin/src/main/resources/commands.yml +++ b/plugin/src/main/resources/commands.yml @@ -30,6 +30,20 @@ set_season: - /customcrops season set - /ccrops season set +get_date: + enable: true + permission: customcrops.command.get_date + usage: + - /customcrops date get + - /ccrops date get + +set_date: + enable: true + permission: customcrops.command.set_date + usage: + - /customcrops date set + - /ccrops date set + debug_data: enable: true permission: customcrops.command.debug diff --git a/plugin/src/main/resources/translations/en.yml b/plugin/src/main/resources/translations/en.yml index 5d09ab179..96d31738f 100644 --- a/plugin/src/main/resources/translations/en.yml +++ b/plugin/src/main/resources/translations/en.yml @@ -39,6 +39,21 @@ argument.parse.failure.aggregate.failure: "Invalid component '': Could not resolve or from ''" argument.parse.failure.namedtextcolor: "'' is not a named text color" command.reload.success: "Reloaded. Took ms." +command.season.get.success: "The season in world [] is []" +command.season.get.failure: "Season is disabled in world []" +command.season.set.success: "Successfully set season to in world []" +command.season.set.failure.disable: "Season is disabled in world []" +command.season.set.failure.reference: "World [] is not the reference world" +command.season.set.failure.other: "Can't set season for world [] because plugin [] takes over the season" +command.season.set.failure.invalid: "Invalid season []" +command.date.get.success: "The date in world [] is []" +command.date.get.failure.disable: "Date is disabled in world []" +command.date.get.failure.other: "Can't get date for world [] because plugin [] takes over the calendar" +command.date.set.success: "Successfully set date to in world []" +command.date.set.failure.disable: "Date is disabled in world []" +command.date.set.failure.reference: "World [] is not the reference world" +command.date.set.failure.other: "Can't set date for world [] because plugin [] takes over the calendar" +command.date.set.failure.invalid: "Invalid date []" season.spring: "Spring" season.summer: "Summer" season.autumn: "Autumn" diff --git a/plugin/src/main/resources/translations/zh_cn.yml b/plugin/src/main/resources/translations/zh_cn.yml index acb944613..96e4aa02d 100644 --- a/plugin/src/main/resources/translations/zh_cn.yml +++ b/plugin/src/main/resources/translations/zh_cn.yml @@ -38,7 +38,21 @@ argument.parse.failure.aggregate.missing: "缺少组件 ''" argument.parse.failure.aggregate.failure: "无效的组件 '': " argument.parse.failure.either: "无法从 '' 解析 " argument.parse.failure.namedtextcolor: "'' 不是颜色代码" -command.reload.success: "重新加载完成.耗时 毫秒译者:jhqwqmc" +command.reload.success: "重新加载完成. 耗时 毫秒" +command.season.get.success: "世界 [] 的季节是 []" +command.season.get.failure: "季节没有在世界 [] 启用" +command.season.set.success: "成功设置世界 [] 的季节为 []" +command.season.set.failure.disable: "季节没有在世界 [] 启用" +command.season.set.failure.reference: "世界 [] 不是同步季节设置的参考世界" +command.season.set.failure.other: "无法设置世界 [] 的季节,原因是插件 [] 接管了季节" +command.season.set.failure.invalid: "无效的季节 []" +command.date.get.success: "世界 [] 的日期是 []" +command.date.get.failure.disable: "日期没有在世界 [] 启用" +command.date.get.failure.other: "无法获取世界 [] 的日期,原因是插件 [] 接管了日历" +command.date.set.success: "成功设置世界 [] 的日期为 []" +command.date.set.failure.reference: "世界 [] 不是同步季节设置的参考世界" +command.date.set.failure.other: "无法设置世界 [] 的日期,原因是插件 [] 接管了日历" +command.date.set.failure.invalid: "无效的日期 []" season.spring: "春" season.summer: "夏" season.autumn: "秋" From 3f98c117fcc4a64e635e8b9d48415177670576d6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:31:29 +0800 Subject: [PATCH 071/329] force tick --- .../customcrops/api/core/MappedRegistry.java | 11 ++ .../customcrops/api/core/Registry.java | 4 + .../customcrops/api/core/block/CropBlock.java | 9 +- .../api/core/block/CrowAttack.java | 38 ++--- .../api/core/block/CustomCropsBlock.java | 2 + .../api/core/block/GreenhouseBlock.java | 13 +- .../customcrops/api/core/block/PotBlock.java | 13 +- .../api/core/block/ScarecrowBlock.java | 13 +- .../api/core/block/SprinklerBlock.java | 9 +- .../customcrops/api/core/item/SeedItem.java | 4 +- .../api/core/item/SprinklerItem.java | 4 +- .../api/core/world/CustomCropsChunk.java | 2 +- .../api/core/world/CustomCropsChunkImpl.java | 4 +- .../api/core/world/CustomCropsWorld.java | 2 + .../api/core/world/CustomCropsWorldImpl.java | 5 + .../common/locale/MessageConstants.java | 3 + .../customcrops/common/util/QuadConsumer.java | 22 +++ gradle.properties | 2 +- .../bukkit/command/BukkitCommandManager.java | 3 +- .../command/feature/ForceTickCommand.java | 132 ++++++++++++++++++ .../bukkit/item/BukkitItemManager.java | 19 ++- plugin/src/main/resources/commands.yml | 7 + plugin/src/main/resources/translations/en.yml | 3 + .../src/main/resources/translations/zh_cn.yml | 3 + 24 files changed, 278 insertions(+), 49 deletions(-) create mode 100644 common/src/main/java/net/momirealms/customcrops/common/util/QuadConsumer.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java index 53994b4d0..153986d32 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java @@ -38,6 +38,7 @@ public MappedRegistry(Key key) { public void register(K key, T value) { byKey.put(key, value); byValue.put(value, key); + byID.add(value); } @Override @@ -67,6 +68,16 @@ public T get(@Nullable K key) { return byKey.get(key); } + @Override + public boolean containsKey(@Nullable K key) { + return byKey.containsKey(key); + } + + @Override + public boolean containsValue(@Nullable T value) { + return byValue.containsKey(value); + } + @NotNull @Override public Iterator iterator() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java index f4ee23fa9..5f7cfa467 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java @@ -30,4 +30,8 @@ public interface Registry extends IdMap { @Nullable T get(@Nullable K key); + + boolean containsKey(@Nullable K key); + + boolean containsValue(@Nullable T value); } 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 34447a88b..474e2a1c9 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 @@ -153,6 +153,11 @@ public void onPlace(WrappedPlaceEvent event) { fixOrGetState(world, pos3, event.placedID()); } + @Override + public boolean isBlockInstance(String id) { + return Registries.STAGE_TO_CROP_UNSAFE.containsKey(id); + } + @Override public void onInteract(WrappedInteractEvent event) { final Player player = event.player(); @@ -287,8 +292,8 @@ public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, id(state, cropConfig.id()); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); return state; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java index bb95d4fc9..181391dad 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java @@ -52,7 +52,7 @@ public CrowAttack(Location location, ItemStack flyModel, ItemStack standModel) { } } this.viewers = viewers.toArray(new Player[0]); - this.cropLocation = location.clone().add(RandomUtils.generateRandomDouble(-0.25, 0.25), 0, RandomUtils.generateRandomDouble(-0.25, 0.25)); + this.cropLocation = LocationUtils.toBlockCenterLocation(location).add(RandomUtils.generateRandomDouble(-0.25, 0.25), 0, RandomUtils.generateRandomDouble(-0.25, 0.25)); float yaw = RandomUtils.generateRandomInt(-180, 180); this.cropLocation.setYaw(yaw); this.flyModel = flyModel; @@ -66,47 +66,39 @@ public CrowAttack(Location location, ItemStack flyModel, ItemStack standModel) { public void start() { if (this.viewers.length == 0) return; - FakeArmorStand fake1 = SparrowHeart.getInstance().createFakeArmorStand(dynamicLocation); - fake1.invisible(true); - fake1.small(true); - fake1.equipment(EquipmentSlot.HEAD, flyModel); - FakeArmorStand fake2 = SparrowHeart.getInstance().createFakeArmorStand(cropLocation); - fake1.invisible(true); - fake1.small(true); - fake1.equipment(EquipmentSlot.HEAD, standModel); + FakeArmorStand fake = SparrowHeart.getInstance().createFakeArmorStand(dynamicLocation); + fake.invisible(true); + fake.small(true); + fake.equipment(EquipmentSlot.HEAD, flyModel); for (Player player : this.viewers) { - fake1.spawn(player); + fake.spawn(player); } this.task = BukkitCustomCropsPlugin.getInstance().getScheduler().asyncRepeating(() -> { timer++; if (timer < 100) { dynamicLocation.add(vectorDown); for (Player player : this.viewers) { - SparrowHeart.getInstance().sendClientSideTeleportEntity(player, dynamicLocation, false, fake1.entityID()); + SparrowHeart.getInstance().sendClientSideTeleportEntity(player, dynamicLocation, false, fake.entityID()); } - } else if (timer == 100){ + } else if (timer == 100) { + fake.equipment(EquipmentSlot.HEAD, standModel); for (Player player : this.viewers) { - fake1.destroy(player); - } - for (Player player : this.viewers) { - fake2.spawn(player); + fake.updateEquipment(player); } } else if (timer == 150) { + fake.equipment(EquipmentSlot.HEAD, flyModel); for (Player player : this.viewers) { - fake2.destroy(player); - } - for (Player player : this.viewers) { - fake1.spawn(player); + fake.updateEquipment(player); } } else if (timer > 150) { dynamicLocation.add(vectorUp); for (Player player : this.viewers) { - SparrowHeart.getInstance().sendClientSideTeleportEntity(player, dynamicLocation, false, fake1.entityID()); + SparrowHeart.getInstance().sendClientSideTeleportEntity(player, dynamicLocation, false, fake.entityID()); } } - if (timer > 300) { + if (timer > 250) { for (Player player : this.viewers) { - fake1.destroy(player); + fake.destroy(player); } task.cancel(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java index d0a41669c..efcf9fb37 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java @@ -43,4 +43,6 @@ public interface CustomCropsBlock { void onBreak(WrappedBreakEvent event); void onPlace(WrappedPlaceEvent event); + + boolean isBlockInstance(String id); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java index d74227688..4728083a5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java @@ -90,12 +90,17 @@ public void onPlace(WrappedPlaceEvent event) { CustomCropsWorld world = event.world(); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); } + @Override + public boolean isBlockInstance(String id) { + return ConfigManager.greenhouse().contains(id); + } + public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) { Optional optional = world.getBlockState(pos3); if (optional.isPresent() && optional.get().type() instanceof GreenhouseBlock) { @@ -104,8 +109,8 @@ public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) CustomCropsBlockState state = createBlockState(); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); return state; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index e29d8eb83..031200134 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -199,13 +199,18 @@ public void onPlace(WrappedPlaceEvent event) { world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); ActionManager.trigger(context, config.placeActions()); } + @Override + public boolean isBlockInstance(String id) { + return Registries.ITEM_TO_POT.containsKey(id); + } + @Override public void onInteract(WrappedInteractEvent event) { PotConfig potConfig = Registries.ITEM_TO_POT.get(event.relatedID()); @@ -291,8 +296,8 @@ public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, water(state, potConfig.isWet(blockID) ? 1 : 0); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); return state; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java index d5cb1402b..1aad9b266 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java @@ -90,12 +90,17 @@ public void onPlace(WrappedPlaceEvent event) { CustomCropsWorld world = event.world(); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); } + @Override + public boolean isBlockInstance(String id) { + return ConfigManager.scarecrow().contains(id); + } + public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) { Optional optional = world.getBlockState(pos3); if (optional.isPresent() && optional.get().type() instanceof ScarecrowBlock) { @@ -104,8 +109,8 @@ public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) CustomCropsBlockState state = createBlockState(); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); return state; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java index c340fd183..dd6baac07 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -137,6 +137,11 @@ public void onPlace(WrappedPlaceEvent event) { ActionManager.trigger(context, config.placeActions()); } + @Override + public boolean isBlockInstance(String id) { + return Registries.ITEM_TO_SPRINKLER.containsKey(id); + } + @Override public void onInteract(WrappedInteractEvent event) { SprinklerConfig config = Registries.ITEM_TO_SPRINKLER.get(event.relatedID()); @@ -225,8 +230,8 @@ public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, water(state, blockID.equals(sprinklerConfig.threeDItemWithWater()) ? 1 : 0); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); return state; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java index 6aa8366de..ba130021a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java @@ -118,8 +118,8 @@ public InteractionResult interactAt(WrappedInteractEvent event) { cropBlock.point(state, point); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java index be564837d..e0cc68a89 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java @@ -111,8 +111,8 @@ public InteractionResult interactAt(WrappedInteractEvent event) { BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(targetLocation), config.existenceForm(), config.threeDItem(), FurnitureRotation.NONE); world.addBlockState(pos3, state).ifPresent(previous -> { BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state.compoundMap().toString() + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous.compoundMap().toString() + "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); ActionManager.trigger(context, config.placeActions()); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java index 16f66ccbc..0ae1cb82f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java @@ -179,7 +179,7 @@ public interface CustomCropsChunk { CustomCropsSection getSection(int sectionID); - Collection sections(); + CustomCropsSection[] sections(); Optional removeSection(int sectionID); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java index ba3269aae..c0d79ae5a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java @@ -257,8 +257,8 @@ public CustomCropsSection getSection(int sectionID) { } @Override - public Collection sections() { - return loadedSections.values(); + public CustomCropsSection[] sections() { + return loadedSections.values().toArray(new CustomCropsSection[0]); } @Override diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java index 33e5cc446..ebb579371 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java @@ -80,6 +80,8 @@ default CustomCropsRegion restoreRegion(RegionPos pos, ConcurrentHashMap clazz); + CustomCropsChunk[] loadedChunks(); + /** * Get the state of the block at a certain location * diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java index 831144cab..94c3a3153 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java @@ -131,6 +131,11 @@ public int getChunkBlockAmount(Pos3 pos3, Class claz } } + @Override + public CustomCropsChunk[] loadedChunks() { + return loadedChunks.values().toArray(new CustomCropsChunk[0]); + } + @NotNull @Override public Optional getBlockState(Pos3 location) { diff --git a/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java b/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java index 4fec42cca..2d6548c8a 100644 --- a/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java +++ b/common/src/main/java/net/momirealms/customcrops/common/locale/MessageConstants.java @@ -43,4 +43,7 @@ public interface MessageConstants { TranslatableComponent.Builder COMMAND_SET_DATE_FAILURE_REFERENCE = Component.translatable().key("command.date.set.failure.reference"); TranslatableComponent.Builder COMMAND_SET_DATE_FAILURE_OTHER = Component.translatable().key("command.date.set.failure.other"); TranslatableComponent.Builder COMMAND_SET_DATE_FAILURE_INVALID = Component.translatable().key("command.date.set.failure.invalid"); + TranslatableComponent.Builder COMMAND_FORCE_TICK_SUCCESS = Component.translatable().key("command.force_tick.success"); + TranslatableComponent.Builder COMMAND_FORCE_TICK_FAILURE_TYPE = Component.translatable().key("command.force_tick.failure.type"); + TranslatableComponent.Builder COMMAND_FORCE_TICK_FAILURE_DISABLE = Component.translatable().key("command.force_tick.failure.disable"); } diff --git a/common/src/main/java/net/momirealms/customcrops/common/util/QuadConsumer.java b/common/src/main/java/net/momirealms/customcrops/common/util/QuadConsumer.java new file mode 100644 index 000000000..0c129be51 --- /dev/null +++ b/common/src/main/java/net/momirealms/customcrops/common/util/QuadConsumer.java @@ -0,0 +1,22 @@ +/* + * 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.common.util; + +public interface QuadConsumer { + void accept(K k, V v, S s, O o); +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 63f69e98f..b2e46075d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,7 +15,7 @@ asm_commons_version=9.7 jar_relocator_version=1.7 adventure_bundle_version=4.17.0 adventure_platform_version=4.3.4 -sparrow_heart_version=0.39 +sparrow_heart_version=0.40 cloud_core_version=2.0.0-rc.2 cloud_services_version=2.0.0-rc.2 cloud_brigadier_version=2.0.0-beta.9 diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java index c7ee24ec4..7b7cd7d28 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java @@ -40,7 +40,8 @@ public class BukkitCommandManager extends AbstractCommandManager new GetSeasonCommand(this), new SetSeasonCommand(this), new GetDateCommand(this), - new SetDateCommand(this) + new SetDateCommand(this), + new ForceTickCommand(this) ); private final Index> INDEX = Index.create(CommandFeature::getFeatureID, FEATURES); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java new file mode 100644 index 000000000..92ea41109 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java @@ -0,0 +1,132 @@ +/* + * 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.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.Registries; +import net.momirealms.customcrops.api.core.block.CustomCropsBlock; +import net.momirealms.customcrops.api.core.world.*; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.locale.MessageConstants; +import net.momirealms.customcrops.common.util.Key; +import net.momirealms.customcrops.common.util.QuadConsumer; +import net.momirealms.customcrops.common.util.TriConsumer; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.NamespacedKeyParser; +import org.incendo.cloud.bukkit.parser.WorldParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.EnumParser; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +public class ForceTickCommand extends BukkitCommandFeature { + + public ForceTickCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("world", WorldParser.worldParser()) + .required("type", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + int all = Registries.BLOCK.size(); + ArrayList blocks = new ArrayList<>(); + for (int i = 0; i < all; i++) { + blocks.add(Registries.BLOCK.byId(i)); + } + return CompletableFuture.completedFuture( + blocks.stream().map(block -> Suggestion.suggestion(block.type().asString())).toList() + ); + } + })) + .required("mode", EnumParser.enumParser(Mode.class)) + .flag(manager.flagBuilder("silent").build()) + .handler(context -> { + World world = context.get("world"); + NamespacedKey type = context.get("type"); + Mode mode = context.get("mode"); + Key key = Key.key(type.asString()); + CustomCropsBlock customCropsBlock = Registries.BLOCK.get(key); + if (customCropsBlock == null) { + handleFeedback(context.sender(), MessageConstants.COMMAND_FORCE_TICK_FAILURE_TYPE, Component.text(key.asString())); + return; + } + Optional> optionalWorld = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(world); + if (optionalWorld.isEmpty()) { + handleFeedback(context.sender(), MessageConstants.COMMAND_FORCE_TICK_FAILURE_DISABLE, Component.text(world.getName())); + return; + } + CustomCropsWorld customCropsWorld = optionalWorld.get(); + BukkitCustomCropsPlugin.getInstance().getScheduler().async().execute(() -> { + int amount = 0; + long time1 = System.currentTimeMillis(); + for (CustomCropsChunk customCropsChunk : customCropsWorld.loadedChunks()) { + for (CustomCropsSection customCropsSection : customCropsChunk.sections()) { + for (Map.Entry entry : customCropsSection.blockMap().entrySet()) { + CustomCropsBlockState state = entry.getValue(); + if (state.type() == customCropsBlock) { + Pos3 pos3 = entry.getKey().toPos3(customCropsChunk.chunkPos()); + mode.consumer.accept(customCropsBlock, state, customCropsWorld, pos3); + amount++; + } + } + } + } + handleFeedback(context.sender(), MessageConstants.COMMAND_FORCE_TICK_SUCCESS, Component.text(System.currentTimeMillis() - time1), Component.text(amount)); + }); + }); + } + + private enum Mode { + + RANDOM_TICK(CustomCropsBlock::randomTick), + SCHEDULED_TICK(CustomCropsBlock::scheduledTick), + ALL((b, s, w, p) -> { + b.randomTick(s, w, p); + b.scheduledTick(s, w, p); + }); + + private final QuadConsumer, Pos3> consumer; + + Mode(QuadConsumer, Pos3> consumer) { + this.consumer = consumer; + } + } + + @Override + public String getFeatureID() { + return "force_tick"; + } +} 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 2c4f86687..83e7a3912 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 @@ -24,7 +24,9 @@ import net.momirealms.customcrops.api.core.block.BreakReason; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; import net.momirealms.customcrops.api.core.item.CustomCropsItem; +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.core.wrapper.WrappedInteractAirEvent; import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent; @@ -454,8 +456,23 @@ public void handlePlayerPlace(Player player, Location location, String placedID, return; } - String itemID = id(itemInHand); CustomCropsWorld world = optionalWorld.get(); + Pos3 pos3 = Pos3.from(location); + Optional optionalState = world.getBlockState(pos3); + if (optionalState.isPresent()) { + CustomCropsBlockState customCropsBlockState = optionalState.get(); + String anyID = anyID(location); + System.out.println(anyID); + if (!customCropsBlockState.type().isBlockInstance(anyID)) { + world.removeBlockState(pos3); + plugin.debug("[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState); + } else { + event.setCancelled(true); + return; + } + } + + String itemID = id(itemInHand); WrappedPlaceEvent wrapped = new WrappedPlaceEvent(player, world, location, placedID, hand, itemInHand, itemID, event); CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(placedID); if (customCropsBlock != null) { diff --git a/plugin/src/main/resources/commands.yml b/plugin/src/main/resources/commands.yml index c5d32aaf3..fe9c732fb 100644 --- a/plugin/src/main/resources/commands.yml +++ b/plugin/src/main/resources/commands.yml @@ -50,3 +50,10 @@ debug_data: usage: - /customcrops debug data - /ccrops debug data + +force_tick: + enable: true + permission: customcrops.command.force_tick + usage: + - /customcrops force-tick + - /ccrops force-tick \ No newline at end of file diff --git a/plugin/src/main/resources/translations/en.yml b/plugin/src/main/resources/translations/en.yml index 96d31738f..58ab14b8a 100644 --- a/plugin/src/main/resources/translations/en.yml +++ b/plugin/src/main/resources/translations/en.yml @@ -54,6 +54,9 @@ command.date.set.failure.disable: "Date is disabled in world []World [] is not the reference world" command.date.set.failure.other: "Can't set date for world [] because plugin [] takes over the calendar" command.date.set.failure.invalid: "Invalid date []" +command.force_tick.success: "Took ms ticking blocks" +command.force_tick.failure.disable: "CustomCrops is not enabled in world []" +command.force_tick.failure.type: "Unknown type []" season.spring: "Spring" season.summer: "Summer" season.autumn: "Autumn" diff --git a/plugin/src/main/resources/translations/zh_cn.yml b/plugin/src/main/resources/translations/zh_cn.yml index 96e4aa02d..702ccf8c4 100644 --- a/plugin/src/main/resources/translations/zh_cn.yml +++ b/plugin/src/main/resources/translations/zh_cn.yml @@ -53,6 +53,9 @@ command.date.set.success: "成功设置世界 [] 的日期为 [世界 [] 不是同步季节设置的参考世界" command.date.set.failure.other: "无法设置世界 [] 的日期,原因是插件 [] 接管了日历" command.date.set.failure.invalid: "无效的日期 []" +command.force_tick.success: "花费 ms 更新了 个方块" +command.force_tick.failure.disable: "CustomCrops没有在世界 [] 启用" +command.force_tick.failure.type: "未知的类型 []" season.spring: "春" season.summer: "夏" season.autumn: "秋" From 32a4ab166fa0f8752ce045114a7bc124c38cf960 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:18:25 +0800 Subject: [PATCH 072/329] Try implementing some events --- .../api/core/AbstractCustomEventListener.java | 246 +++++++++++++++++- .../api/core/AbstractItemManager.java | 22 ++ .../customcrops/api/core/ConfigManager.java | 11 +- .../api/core/block/CrowAttack.java | 9 +- .../api/event/BoneMealDispenseEvent.java | 121 +++++++++ .../bukkit/config/BukkitConfigManager.java | 4 +- .../bukkit/item/BukkitItemManager.java | 60 ++++- 7 files changed, 455 insertions(+), 18 deletions(-) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java index 58f57d21b..dcf5e1491 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -17,20 +17,40 @@ package net.momirealms.customcrops.api.core; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.block.*; +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.event.BoneMealDispenseEvent; +import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.common.helper.VersionHelper; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.Dispenser; +import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.*; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; -import java.util.HashSet; -import java.util.List; +import java.util.*; public abstract class AbstractCustomEventListener implements Listener { @@ -135,4 +155,220 @@ public void onBreakBlock(BlockBreakEvent event) { event ); } + + @EventHandler (ignoreCancelled = true) + public void onItemSpawn(ItemSpawnEvent event) { + Item item = event.getEntity(); + ItemStack itemStack = item.getItemStack(); + String itemID = this.itemManager.id(itemStack); + + if (Registries.STAGE_TO_CROP_UNSAFE.containsKey(itemID)) { + event.setCancelled(true); + return; + } + + SprinklerConfig sprinkler = Registries.ITEM_TO_SPRINKLER.get(itemID); + if (sprinkler != null) { + String twoD = sprinkler.twoDItem(); + if (twoD != null) { + if (!twoD.equals(itemID)) { + ItemStack newItem = this.itemManager.build(null, twoD); + if (newItem != null && newItem.getType() != Material.AIR) { + newItem.setAmount(itemStack.getAmount()); + item.setItemStack(newItem); + } + return; + } + } + } + + PotConfig pot = Registries.ITEM_TO_POT.get(itemID); + if (pot != null) { + ItemStack newItem = this.itemManager.build(null, pot.getPotAppearance(false, null)); + if (newItem != null && newItem.getType() != Material.AIR) { + newItem.setAmount(itemStack.getAmount()); + item.setItemStack(newItem); + } + return; + } + } + + @EventHandler (ignoreCancelled = true) + public void onBlockChange(BlockFadeEvent event) { + Block block = event.getBlock(); + if (block.getType() == Material.FARMLAND) { + Pos3 above = Pos3.from(block.getLocation()).add(0,1,0); + BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(block.getWorld()) + .flatMap(world -> world.getBlockState(above)).ifPresent(blockState -> { + if (blockState.type() instanceof CropBlock) { + event.setCancelled(true); + } + }); + } + } + + @EventHandler (ignoreCancelled = true) + public void onTrampling(EntityChangeBlockEvent event) { + Block block = event.getBlock(); + if (block.getType() == Material.FARMLAND && event.getTo() == Material.DIRT) { + if (ConfigManager.preventTrampling()) { + event.setCancelled(true); + return; + } + this.itemManager.handleEntityTrample(event.getEntity(), block.getLocation(), "FARMLAND", event); + } + } + + @EventHandler (ignoreCancelled = true) + public void onMoistureChange(MoistureChangeEvent event) { + if (ConfigManager.disableMoistureMechanic()) + event.setCancelled(true); + } + + @EventHandler (ignoreCancelled = true) + public void onPistonExtend(BlockPistonExtendEvent event) { + Optional> world = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(event.getBlock().getWorld()); + if (world.isEmpty()){ + return; + } + CustomCropsWorld w = world.get(); + for (Block block : event.getBlocks()) { + if (w.getBlockState(Pos3.from(block.getLocation())).isPresent()) { + event.setCancelled(true); + return; + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onPistonRetract(BlockPistonRetractEvent event) { + Optional> world = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(event.getBlock().getWorld()); + if (world.isEmpty()){ + return; + } + CustomCropsWorld w = world.get(); + for (Block block : event.getBlocks()) { + if (w.getBlockState(Pos3.from(block.getLocation())).isPresent()) { + event.setCancelled(true); + return; + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onItemDamage(PlayerItemDamageEvent event) { + ItemStack itemStack = event.getItem(); + String itemID = this.itemManager.id(itemStack); + if (Registries.ITEM_TO_WATERING_CAN.containsKey(itemID)) { + event.setCancelled(true); + } + } + + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onExplosion(EntityExplodeEvent event) { + Entity entity = event.getEntity(); + for (Block block : event.blockList()) { + this.itemManager.handleEntityExplode(entity, block.getLocation(), this.itemManager.blockID(block), event); + if (event.isCancelled()) { + return; + } + } + } + + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onExplosion(BlockExplodeEvent event) { + Block exploder = event.getBlock(); + for (Block block : event.blockList()) { + this.itemManager.handleBlockExplode(exploder, block.getLocation(), this.itemManager.blockID(block), event); + if (event.isCancelled()) { + return; + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onDispenser(BlockDispenseEvent event) { + Block block = event.getBlock(); + if (!(block.getBlockData() instanceof org.bukkit.block.data.type.Dispenser directional)) { + return; + } + Optional> optionalWorld = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(event.getBlock().getWorld()); + if (optionalWorld.isEmpty()){ + return; + } + CustomCropsWorld world = optionalWorld.get(); + Block relative = block.getRelative(directional.getFacing()); + Location location = relative.getLocation(); + Pos3 pos3 = Pos3.from(location); + world.getBlockState(pos3).ifPresent(state -> { + if (state.type() instanceof CropBlock cropBlock) { + ItemStack itemStack = event.getItem(); + String itemID = itemManager.id(itemStack); + CropConfig cropConfig = cropBlock.config(state); + int point = cropBlock.point(state); + if (point < cropConfig.maxPoints()) { + for (BoneMeal boneMeal : cropConfig.boneMeals()) { + if (boneMeal.isDispenserAllowed()) { + if (EventUtils.fireAndCheckCancel(new BoneMealDispenseEvent(block, itemStack, location, boneMeal, state))) { + event.setCancelled(true); + return; + } + if (block.getState() instanceof Dispenser dispenser) { + event.setCancelled(true); + Inventory inventory = dispenser.getInventory(); + for (ItemStack storage : inventory.getStorageContents()) { + if (storage == null) continue; + String id = itemManager.id(storage); + if (id.equals(itemID)) { + storage.setAmount(storage.getAmount() - 1); + Context context = Context.player(null); + context.arg(ContextKeys.LOCATION, location); + boneMeal.triggerActions(context); + + int afterPoints = Math.min(point + boneMeal.rollPoint(), cropConfig.maxPoints()); + cropBlock.point(state, afterPoints); + + String afterStage = null; + ExistenceForm afterForm = null; + int tempPoints = afterPoints; + while (tempPoints >= 0) { + Map.Entry afterEntry = cropConfig.getFloorStageEntry(tempPoints); + CropStageConfig after = afterEntry.getValue(); + if (after.stageID() != null) { + afterStage = after.stageID(); + afterForm = after.existenceForm(); + break; + } + tempPoints = after.point() - 1; + } + + Objects.requireNonNull(afterForm); + Objects.requireNonNull(afterStage); + + Context blockContext = Context.block(state); + blockContext.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); + for (int i = point + 1; i <= afterPoints; i++) { + CropStageConfig stage = cropConfig.stageByPoint(i); + if (stage != null) { + ActionManager.trigger(blockContext, stage.growActions()); + } + } + + // TODO if the block model chanegs + Location bukkitLocation = location.toLocation(world.bukkitWorld()); + FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); + if (rotation == FurnitureRotation.NONE && cropConfig.rotation()) { + rotation = FurnitureRotation.random(); + } + BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, afterForm, afterStage, rotation); + } + } + } + return; + } + } + } + } + }); + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java index b8f3a23c5..9ebf7240b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractItemManager.java @@ -20,6 +20,7 @@ import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.inventory.EquipmentSlot; @@ -61,6 +62,27 @@ public abstract void handlePlayerBreak( Cancellable event ); + public abstract void handleEntityTrample( + Entity entity, + Location location, + String brokenID, + Cancellable event + ); + + public abstract void handleEntityExplode( + Entity entity, + Location location, + String brokenID, + Cancellable event + ); + + public abstract void handleBlockExplode( + Block block, + Location location, + String brokenID, + Cancellable event + ); + public abstract void handlePlayerPlace( Player player, Location location, diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java index 7eec0d24f..6e05e1414 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -61,7 +61,8 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable { protected double[] defaultQualityRatio; - protected boolean hasNamespace; + protected boolean preventTrampling; + protected boolean disableMoistureMechanic; public ConfigManager(BukkitCustomCropsPlugin plugin) { this.plugin = plugin; @@ -144,8 +145,12 @@ public static double[] defaultQualityRatio() { return instance.defaultQualityRatio; } - public static boolean hasNamespace() { - return instance.hasNamespace; + public static boolean preventTrampling() { + return instance.preventTrampling; + } + + public static boolean disableMoistureMechanic() { + return instance.disableMoistureMechanic; } @Override diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java index 181391dad..51d571cfa 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java @@ -36,7 +36,6 @@ public class CrowAttack { private SchedulerTask task; private final Location dynamicLocation; - private final Location cropLocation; private final Vector vectorDown; private final Vector vectorUp; private final Player[] viewers; @@ -52,14 +51,14 @@ public CrowAttack(Location location, ItemStack flyModel, ItemStack standModel) { } } this.viewers = viewers.toArray(new Player[0]); - this.cropLocation = LocationUtils.toBlockCenterLocation(location).add(RandomUtils.generateRandomDouble(-0.25, 0.25), 0, RandomUtils.generateRandomDouble(-0.25, 0.25)); + Location landLocation = LocationUtils.toBlockCenterLocation(location).add(RandomUtils.generateRandomDouble(-0.25, 0.25), 0, RandomUtils.generateRandomDouble(-0.25, 0.25)); float yaw = RandomUtils.generateRandomInt(-180, 180); - this.cropLocation.setYaw(yaw); + landLocation.setYaw(yaw); this.flyModel = flyModel; this.standModel = standModel; - this.dynamicLocation = cropLocation.clone().add((10 * Math.sin((Math.PI * yaw) / 180)), 10, (- 10 * Math.cos((Math.PI * yaw) / 180))); + this.dynamicLocation = landLocation.clone().add((10 * Math.sin((Math.PI * yaw) / 180)), 10, (- 10 * Math.cos((Math.PI * yaw) / 180))); this.dynamicLocation.setYaw(yaw); - Location relative = cropLocation.clone().subtract(dynamicLocation); + Location relative = landLocation.clone().subtract(dynamicLocation); this.vectorDown = new Vector(relative.getX() / 100, -0.1, relative.getZ() / 100); this.vectorUp = new Vector(relative.getX() / 100, 0.1, relative.getZ() / 100); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java new file mode 100644 index 000000000..9ea3a5541 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) <2022> + * + * 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.event; + +import net.momirealms.customcrops.api.core.block.BoneMeal; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +/** + * An event that triggered when the bone meal is dispensed + */ +public class BoneMealDispenseEvent extends Event implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Location location; + private final BoneMeal boneMeal; + private final CustomCropsBlockState blockState; + private final ItemStack boneMealItem; + private final Block dispenser; + + public BoneMealDispenseEvent( + @NotNull Block dispenser, + @NotNull ItemStack boneMealItem, + @NotNull Location location, + @NotNull BoneMeal boneMeal, + @NotNull CustomCropsBlockState blockState + ) { + this.location = location; + this.blockState = blockState; + this.boneMeal = boneMeal; + this.boneMealItem = boneMealItem; + this.dispenser = dispenser; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return getHandlerList(); + } + + /** + * Get the crop location + * @return location + */ + @NotNull + public Location getLocation() { + return location; + } + + /** + * Get the item in player's hand + * If there's nothing in hand, it would return AIR + * @return item in hand + */ + @NotNull + public ItemStack getBoneMealItem() { + return boneMealItem; + } + + @NotNull + public CustomCropsBlockState getBlockState() { + return blockState; + } + + /** + * Get the bone meal's config + * + * @return bone meal config + */ + @NotNull + public BoneMeal getBoneMeal() { + return boneMeal; + } + + /** + * Get the dispenser block + * + * @return dispenser block + */ + @NotNull + public Block getDispenser() { + return dispenser; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java index b5ca93c69..283277fcf 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -36,7 +36,6 @@ import net.momirealms.customcrops.api.core.block.SprinklerConfig; import net.momirealms.customcrops.api.core.item.FertilizerConfig; import net.momirealms.customcrops.api.core.item.WateringCanConfig; -import net.momirealms.customcrops.api.util.PluginUtils; import net.momirealms.customcrops.common.helper.AdventureHelper; import net.momirealms.customcrops.common.locale.TranslationManager; import net.momirealms.customcrops.common.plugin.CustomCropsProperties; @@ -131,7 +130,8 @@ private void loadSettings() { defaultQualityRatio = getQualityRatio(config.getString("mechanics.default-quality-ratio", "17/2/1")); - hasNamespace = PluginUtils.isEnabled("ItemsAdder"); + preventTrampling = config.getBoolean("mechanics.vanilla-farmland.prevent-trampling", false); + disableMoistureMechanic = config.getBoolean("mechanics.vanilla-farmland.disable-moisture-mechanic", false); } @Override 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 83e7a3912..90c6a94ab 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 @@ -449,6 +449,51 @@ public void handlePlayerBreak(Player player, Location location, ItemStack itemIn } } + @Override + public void handleEntityTrample(Entity entity, Location location, String brokenID, Cancellable event) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(entity.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + + CustomCropsWorld world = optionalWorld.get(); + WrappedBreakEvent wrapped = new WrappedBreakEvent(entity, null, world, location, brokenID, null, null, BreakReason.TRAMPLE, event); + CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(brokenID); + if (customCropsBlock != null) { + customCropsBlock.onBreak(wrapped); + } + } + + @Override + public void handleEntityExplode(Entity entity, Location location, String brokenID, Cancellable event) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(entity.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + + CustomCropsWorld world = optionalWorld.get(); + WrappedBreakEvent wrapped = new WrappedBreakEvent(entity, null, world, location, brokenID, null, null, BreakReason.EXPLODE, event); + CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(brokenID); + if (customCropsBlock != null) { + customCropsBlock.onBreak(wrapped); + } + } + + @Override + public void handleBlockExplode(Block block, Location location, String brokenID, Cancellable event) { + Optional> optionalWorld = plugin.getWorldManager().getWorld(block.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + + CustomCropsWorld world = optionalWorld.get(); + WrappedBreakEvent wrapped = new WrappedBreakEvent(null, block, world, location, brokenID, null, null, BreakReason.EXPLODE, event); + CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(brokenID); + if (customCropsBlock != null) { + customCropsBlock.onBreak(wrapped); + } + } + @Override public void handlePlayerPlace(Player player, Location location, String placedID, EquipmentSlot hand, ItemStack itemInHand, Cancellable event) { Optional> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld()); @@ -461,9 +506,18 @@ public void handlePlayerPlace(Player player, Location location, String placedID, Optional optionalState = world.getBlockState(pos3); if (optionalState.isPresent()) { CustomCropsBlockState customCropsBlockState = optionalState.get(); - String anyID = anyID(location); - System.out.println(anyID); - if (!customCropsBlockState.type().isBlockInstance(anyID)) { + String anyFurnitureID = furnitureID(location); + if (anyFurnitureID != null) { + if (!customCropsBlockState.type().isBlockInstance(anyFurnitureID)) { + world.removeBlockState(pos3); + plugin.debug("[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState); + } else { + event.setCancelled(true); + return; + } + } + String anyBlockID = blockID(location); + if (!customCropsBlockState.type().isBlockInstance(anyBlockID)) { world.removeBlockState(pos3); plugin.debug("[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState); } else { From a21af25570065e3212d261c54f3911766e7638bc Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Tue, 3 Sep 2024 01:28:01 +0800 Subject: [PATCH 073/329] Implement logic for dead crops --- .../api/action/AbstractActionManager.java | 13 +--- .../customcrops/api/context/ContextKeys.java | 3 + .../api/core/AbstractCustomEventListener.java | 31 +++------ .../api/core/BuiltInBlockMechanics.java | 1 + .../customcrops/api/core/ConfigManager.java | 5 ++ .../customcrops/api/core/Registries.java | 1 + .../customcrops/api/core/block/CropBlock.java | 60 ++--------------- .../customcrops/api/core/block/DeadCrop.java | 66 +++++++++++++++++++ .../customcrops/api/core/block/PotBlock.java | 15 +++++ .../api/core/item/WateringCanItem.java | 4 +- .../api/core/world/CustomCropsChunk.java | 1 - .../bukkit/BukkitCustomCropsPluginImpl.java | 1 + .../command/feature/ForceTickCommand.java | 2 - .../command/feature/GetDateCommand.java | 1 - .../command/feature/SetDateCommand.java | 5 -- .../command/feature/SetSeasonCommand.java | 4 -- .../bukkit/config/BukkitConfigManager.java | 27 ++++++-- plugin/src/main/resources/config.yml | 3 + 18 files changed, 137 insertions(+), 106 deletions(-) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java index 033a9d115..e808a586e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java @@ -758,17 +758,8 @@ protected void registerHologramAction() { return; } if (applyCorrection) { - Optional optionalState = optionalWorld.get().getBlockState(pos3); - if (optionalState.isPresent()) { - if (optionalState.get().type() instanceof CropBlock cropBlock) { - CropConfig config = cropBlock.config(optionalState.get()); - int point = cropBlock.point(optionalState.get()); - if (config != null) { - CropStageConfig stageConfig = config.stageWithModelByPoint(point); - location.add(0,stageConfig.displayInfoOffset(),0); - } - } - } + String itemID = plugin.getItemManager().anyID(location.clone().add(0,1,0)); + location.add(0,ConfigManager.getOffset(itemID),0); } ArrayList viewers = new ArrayList<>(); if (onlyShowToOne) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java index dbe6cf8fd..8a846b084 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java @@ -42,6 +42,9 @@ public class ContextKeys { public static final ContextKeys SLOT = of("slot", EquipmentSlot.class); public static final ContextKeys TEMP_NEAR_PLAYER = of("near", String.class); public static final ContextKeys OFFLINE = of("offline", Boolean.class); + public static final ContextKeys ICON = of("icon", String.class); + public static final ContextKeys MAX_TIMES = of("max_times", Integer.class); + public static final ContextKeys LEFT_TIMES = of("left_times", Integer.class); public static final ContextKeys PLAYER_INSTANCE = of("player_instance", Player.class); private final String key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java index dcf5e1491..d1f5309df 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -50,7 +50,10 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; public abstract class AbstractCustomEventListener implements Listener { @@ -324,27 +327,8 @@ public void onDispenser(BlockDispenseEvent event) { Context context = Context.player(null); context.arg(ContextKeys.LOCATION, location); boneMeal.triggerActions(context); - int afterPoints = Math.min(point + boneMeal.rollPoint(), cropConfig.maxPoints()); cropBlock.point(state, afterPoints); - - String afterStage = null; - ExistenceForm afterForm = null; - int tempPoints = afterPoints; - while (tempPoints >= 0) { - Map.Entry afterEntry = cropConfig.getFloorStageEntry(tempPoints); - CropStageConfig after = afterEntry.getValue(); - if (after.stageID() != null) { - afterStage = after.stageID(); - afterForm = after.existenceForm(); - break; - } - tempPoints = after.point() - 1; - } - - Objects.requireNonNull(afterForm); - Objects.requireNonNull(afterStage); - Context blockContext = Context.block(state); blockContext.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); for (int i = point + 1; i <= afterPoints; i++) { @@ -353,14 +337,15 @@ public void onDispenser(BlockDispenseEvent event) { ActionManager.trigger(blockContext, stage.growActions()); } } - - // TODO if the block model chanegs + CropStageConfig currentStage = cropConfig.stageWithModelByPoint(point); + CropStageConfig afterStage = cropConfig.stageWithModelByPoint(afterPoints); + if (currentStage == afterStage) return; Location bukkitLocation = location.toLocation(world.bukkitWorld()); FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); if (rotation == FurnitureRotation.NONE && cropConfig.rotation()) { rotation = FurnitureRotation.random(); } - BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, afterForm, afterStage, rotation); + BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, afterStage.existenceForm(), Objects.requireNonNull(afterStage.stageID()), rotation); } } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java index 986697d58..3bbc3ebeb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java @@ -27,6 +27,7 @@ public class BuiltInBlockMechanics { public static final BuiltInBlockMechanics CROP = create("crop"); + public static final BuiltInBlockMechanics DEAD_CROP = create("dead_crop"); public static final BuiltInBlockMechanics SPRINKLER = create("sprinkler"); public static final BuiltInBlockMechanics GREENHOUSE = create("greenhouse"); public static final BuiltInBlockMechanics POT = create("pot"); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java index 6e05e1414..7d9d4fd3c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -63,12 +63,17 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable { protected boolean preventTrampling; protected boolean disableMoistureMechanic; + protected HashMap offsets = new HashMap<>(); public ConfigManager(BukkitCustomCropsPlugin plugin) { this.plugin = plugin; instance = this; } + public static double getOffset(String id) { + return instance.offsets.getOrDefault(id, 0d); + } + public static boolean syncSeasons() { return instance.syncSeasons; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java index d9a7884ab..7a2f19226 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java @@ -57,4 +57,5 @@ public class Registries { public static final ClearableRegistry ITEM_TO_POT = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_pot")); public static final ClearableRegistry ITEM_TO_SPRINKLER = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_sprinkler")); public static final ClearableRegistry ITEM_TO_WATERING_CAN = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_wateringcan")); + public static final ClearableRegistry ITEM_TO_DEAD_CROP = new ClearableMappedRegistry<>(Key.key("fast_lookup", "item_to_dead_crop")); } 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 474e2a1c9..3f474b447 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 @@ -225,22 +225,7 @@ public void onInteract(WrappedInteractEvent event) { int afterPoints = Math.min(point + boneMeal.rollPoint(), cropConfig.maxPoints()); point(state, afterPoints); - String afterStage = null; - ExistenceForm afterForm = null; - int tempPoints = afterPoints; - while (tempPoints >= 0) { - Map.Entry afterEntry = cropConfig.getFloorStageEntry(tempPoints); - CropStageConfig after = afterEntry.getValue(); - if (after.stageID() != null) { - afterStage = after.stageID(); - afterForm = after.existenceForm(); - break; - } - tempPoints = after.point() - 1; - } - - Objects.requireNonNull(afterForm); - Objects.requireNonNull(afterStage); + CropStageConfig nextStage = cropConfig.stageWithModelByPoint(afterPoints); Context blockContext = Context.block(state); blockContext.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); @@ -251,13 +236,13 @@ public void onInteract(WrappedInteractEvent event) { } } - if (Objects.equals(afterStage, event.relatedID())) return; + if (Objects.equals(nextStage.stageID(), event.relatedID())) return; Location bukkitLocation = location.toLocation(world.bukkitWorld()); FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); if (rotation == FurnitureRotation.NONE && cropConfig.rotation()) { rotation = FurnitureRotation.random(); } - BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, afterForm, afterStage, rotation); + BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, nextStage.existenceForm(), Objects.requireNonNull(nextStage.stageID()), rotation); return; } } @@ -363,39 +348,8 @@ private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Po int afterPoints = Math.min(previousPoint + pointToAdd, config.maxPoints()); point(state, afterPoints); - int tempPoints = previousPoint; - String preStage = null; - while (tempPoints >= 0) { - Map.Entry preEntry = config.getFloorStageEntry(tempPoints); - CropStageConfig pre = preEntry.getValue(); - if (pre.stageID() != null) { - preStage = pre.stageID(); - break; - } - tempPoints = pre.point() - 1; - } - - String afterStage = null; - ExistenceForm afterForm = null; - tempPoints = afterPoints; - while (tempPoints >= 0) { - Map.Entry afterEntry = config.getFloorStageEntry(tempPoints); - CropStageConfig after = afterEntry.getValue(); - if (after.stageID() != null) { - afterStage = after.stageID(); - afterForm = after.existenceForm(); - break; - } - tempPoints = after.point() - 1; - } - - final String finalPreStage = preStage; - final String finalAfterStage = afterStage; - final ExistenceForm finalAfterForm = afterForm; - - Objects.requireNonNull(finalAfterStage); - Objects.requireNonNull(finalPreStage); - Objects.requireNonNull(finalAfterForm); + CropStageConfig currentStage = config.stageWithModelByPoint(previousPoint); + CropStageConfig nextStage = config.stageWithModelByPoint(afterPoints); BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run(() -> { for (int i = previousPoint + 1; i <= afterPoints; i++) { @@ -404,12 +358,12 @@ private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Po ActionManager.trigger(context, stage.growActions()); } } - if (Objects.equals(finalAfterStage, finalPreStage)) return; + if (currentStage == nextStage) return; FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); if (rotation == FurnitureRotation.NONE && config.rotation()) { rotation = FurnitureRotation.random(); } - BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, finalAfterForm, finalAfterStage, rotation); + BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, nextStage.existenceForm(), Objects.requireNonNull(nextStage.stageID()), rotation); }, bukkitLocation); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java new file mode 100644 index 000000000..9d2bdda08 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java @@ -0,0 +1,66 @@ +/* + * 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.core.block; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; +import net.momirealms.customcrops.api.core.Registries; +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.WrappedInteractEvent; +import net.momirealms.customcrops.api.util.LocationUtils; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class DeadCrop extends AbstractCustomCropsBlock { + + public DeadCrop() { + super(BuiltInBlockMechanics.DEAD_CROP.key()); + } + + @Override + public boolean isBlockInstance(String id) { + return Registries.ITEM_TO_DEAD_CROP.containsKey(id); + } + + @Override + public void onInteract(WrappedInteractEvent event) { + final Player player = event.player(); + Context context = Context.player(player); + // data first + CustomCropsWorld world = event.world(); + Location location = event.location(); + context.arg(ContextKeys.SLOT, event.hand()); + // check pot below + Location potLocation = location.clone().subtract(0,1,0); + String blockBelowID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(potLocation.getBlock()); + PotConfig potConfig = Registries.ITEM_TO_POT.get(blockBelowID); + if (potConfig != null) { + context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(potLocation)); + PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); + assert potBlock != null; + // fix or get data + CustomCropsBlockState potState = potBlock.fixOrGetState(world, Pos3.from(potLocation), potConfig, event.relatedID()); + if (potBlock.tryWateringPot(player, context, potState, event.hand(), event.itemID(), potConfig, potLocation, event.itemInHand())) + return; + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 031200134..c9a90b0c2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -245,6 +245,21 @@ public void onInteract(WrappedInteractEvent event) { if (tryWateringPot(player, context, state, event.hand(), event.itemID(), potConfig, location, itemInHand)) return; + int water = water(state); + context.arg(ContextKeys.STORAGE, potConfig.storage()); + context.arg(ContextKeys.CURRENT_WATER, water); + context.arg(ContextKeys.WATER_BAR, Optional.ofNullable(potConfig.waterBar()).map(it -> it.getWaterBar(water, potConfig.storage())).orElse("")); + Fertilizer[] fertilizers = fertilizers(state); + // for backward compatibility + for (Fertilizer latest : fertilizers) { + FertilizerConfig config = latest.config(); + if (config != null) { + context.arg(ContextKeys.ICON, config.icon()); + context.arg(ContextKeys.MAX_TIMES, config.times()); + context.arg(ContextKeys.LEFT_TIMES, latest.times()); + break; + } + } ActionManager.trigger(context, potConfig.interactActions()); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java index e06f87dd7..aaa19539c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java @@ -239,8 +239,8 @@ public InteractionResult interactAt(WrappedInteractEvent event) { } // if the clicked block is a crop, correct the target block - List cropConfigs = Registries.STAGE_TO_CROP_UNSAFE.get(event.relatedID()); - if (cropConfigs != null) { + List cropConfigs = Registries.STAGE_TO_CROP_UNSAFE.get(targetBlockID); + if (cropConfigs != null || Registries.ITEM_TO_DEAD_CROP.containsKey(targetBlockID)) { // is a crop targetLocation = targetLocation.subtract(0,1,0); targetBlockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(targetLocation); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java index 0ae1cb82f..3adc0b01a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java @@ -19,7 +19,6 @@ import org.jetbrains.annotations.NotNull; -import java.util.Collection; import java.util.Optional; import java.util.PriorityQueue; import java.util.Set; 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 128eecfcb..b92c28ccd 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -232,6 +232,7 @@ private void registerDefaultMechanics() { registryAccess.registerBlockMechanic(new ScarecrowBlock()); registryAccess.registerBlockMechanic(new SprinklerBlock()); registryAccess.registerBlockMechanic(new GreenhouseBlock()); + registryAccess.registerBlockMechanic(new DeadCrop()); registryAccess.registerItemMechanic(new SeedItem()); registryAccess.registerItemMechanic(new WateringCanItem()); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java index 92ea41109..0355b927c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java @@ -27,7 +27,6 @@ import net.momirealms.customcrops.common.locale.MessageConstants; import net.momirealms.customcrops.common.util.Key; import net.momirealms.customcrops.common.util.QuadConsumer; -import net.momirealms.customcrops.common.util.TriConsumer; import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.command.CommandSender; @@ -39,7 +38,6 @@ import org.incendo.cloud.context.CommandContext; import org.incendo.cloud.context.CommandInput; import org.incendo.cloud.parser.standard.EnumParser; -import org.incendo.cloud.parser.standard.StringParser; import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.SuggestionProvider; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetDateCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetDateCommand.java index f156d0260..db163c5c3 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetDateCommand.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/GetDateCommand.java @@ -19,7 +19,6 @@ import net.kyori.adventure.text.Component; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; -import net.momirealms.customcrops.api.core.world.Season; import net.momirealms.customcrops.api.integration.SeasonProvider; import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; import net.momirealms.customcrops.common.command.CustomCropsCommandManager; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetDateCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetDateCommand.java index 2acfa1060..3a9207ebc 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetDateCommand.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetDateCommand.java @@ -21,7 +21,6 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.ConfigManager; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; -import net.momirealms.customcrops.api.core.world.Season; import net.momirealms.customcrops.api.integration.SeasonProvider; import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; import net.momirealms.customcrops.common.command.CustomCropsCommandManager; @@ -32,11 +31,7 @@ import org.incendo.cloud.CommandManager; import org.incendo.cloud.bukkit.parser.WorldParser; import org.incendo.cloud.parser.standard.IntegerParser; -import org.incendo.cloud.parser.standard.StringParser; -import org.incendo.cloud.suggestion.Suggestion; -import org.incendo.cloud.suggestion.SuggestionProvider; -import java.util.Locale; import java.util.Optional; public class SetDateCommand extends BukkitCommandFeature { diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetSeasonCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetSeasonCommand.java index c5bf431ec..df337be53 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetSeasonCommand.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/SetSeasonCommand.java @@ -28,19 +28,15 @@ import net.momirealms.customcrops.common.locale.MessageConstants; import org.bukkit.World; import org.bukkit.command.CommandSender; -import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; import org.incendo.cloud.CommandManager; import org.incendo.cloud.bukkit.parser.WorldParser; -import org.incendo.cloud.context.CommandContext; -import org.incendo.cloud.context.CommandInput; import org.incendo.cloud.parser.standard.StringParser; import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.SuggestionProvider; import java.util.Locale; import java.util.Optional; -import java.util.concurrent.CompletableFuture; public class SetSeasonCommand extends BukkitCommandFeature { diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java index 283277fcf..6c3a726dd 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -30,10 +30,7 @@ import dev.dejvokep.boostedyaml.utils.format.NodeRole; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.block.CropConfig; -import net.momirealms.customcrops.api.core.block.CropStageConfig; -import net.momirealms.customcrops.api.core.block.PotConfig; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.block.*; import net.momirealms.customcrops.api.core.item.FertilizerConfig; import net.momirealms.customcrops.api.core.item.WateringCanConfig; import net.momirealms.customcrops.common.helper.AdventureHelper; @@ -132,6 +129,16 @@ private void loadSettings() { preventTrampling = config.getBoolean("mechanics.vanilla-farmland.prevent-trampling", false); disableMoistureMechanic = config.getBoolean("mechanics.vanilla-farmland.disable-moisture-mechanic", false); + + offsets.clear(); + Section section = config.getSection("mechanics.hologram-offset-correction"); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Number n) { + offsets.put(entry.getKey(), n.doubleValue()); + } + } + } } @Override @@ -228,9 +235,21 @@ public void registerCropConfig(CropConfig config) { Registries.CROP.register(config.id(), config); Registries.SEED_TO_CROP.register(config.seed(), config); Registries.ITEMS.register(config.seed(), BuiltInItemMechanics.SEED.mechanic()); + for (DeathCondition condition : config.deathConditions()) { + String deadStage = condition.deathStage(); + if (deadStage != null) { + if (!Registries.BLOCKS.containsKey(deadStage)) { + Registries.BLOCKS.register(deadStage, BuiltInBlockMechanics.DEAD_CROP.mechanic()); + } + if (!Registries.ITEM_TO_DEAD_CROP.containsKey(deadStage)) { + Registries.ITEM_TO_DEAD_CROP.register(deadStage, 0); + } + } + } for (CropStageConfig stageConfig : config.stages()) { String stageID = stageConfig.stageID(); if (stageID != null) { + offsets.put(stageID, stageConfig.displayInfoOffset()); List list = Registries.STAGE_TO_CROP_UNSAFE.get(stageID); if (list != null) { list.add(config); diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index cc585c971..8e412ebab 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -117,6 +117,9 @@ mechanics: disable-moisture-mechanic: false # Prevent entities from trampling the farmland prevent-trampling: false + # Set hologram offset correction for other blocks + hologram-offset-correction: + "{0}crop_stage_death": 0 other-settings: # It's recommended to use MiniMessage format. If you insist on using legacy color code "&", enable the support below. # Disable this would improve performance From 67bbeb57737b8158b172724f2f629a624e4df869 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Tue, 3 Sep 2024 01:43:05 +0800 Subject: [PATCH 074/329] Fix pot's bug --- .../customcrops/api/core/block/PotBlock.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index c9a90b0c2..01120f593 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -80,7 +80,8 @@ public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, P @Override public void onBreak(WrappedBreakEvent event) { CustomCropsWorld world = event.world(); - Pos3 pos3 = Pos3.from(event.location()); + Location location = event.location(); + Pos3 pos3 = Pos3.from(location); PotConfig config = Registries.ITEM_TO_POT.get(event.brokenID()); if (config == null) { world.removeBlockState(pos3); @@ -89,12 +90,15 @@ public void onBreak(WrappedBreakEvent event) { final Player player = event.playerBreaker(); Context context = Context.player(player); + + context.arg(ContextKeys.LOCATION, location); if (!RequirementManager.isSatisfied(context, config.breakRequirements())) { event.setCancelled(true); return; } - Location upperLocation = event.location().clone().add(0,1,0); + Location upperLocation = location.clone().add(0,1,0); + String upperID = BukkitCustomCropsPlugin.getInstance().getItemManager().anyID(upperLocation); List cropConfigs = Registries.STAGE_TO_CROP_UNSAFE.get(upperID); @@ -122,6 +126,7 @@ public void onBreak(WrappedBreakEvent event) { cropConfig = cropConfigs.get(0); } + context.arg(ContextKeys.LOCATION, upperLocation); if (!RequirementManager.isSatisfied(context, cropConfig.breakRequirements())) { event.setCancelled(true); return; @@ -150,7 +155,6 @@ public void onBreak(WrappedBreakEvent event) { return; } - ActionManager.trigger(context, config.breakActions()); if (stageConfig != null) { ActionManager.trigger(context, stageConfig.breakActions()); } @@ -160,6 +164,9 @@ public void onBreak(WrappedBreakEvent event) { BukkitCustomCropsPlugin.getInstance().getItemManager().remove(upperLocation, ExistenceForm.ANY); } + context.arg(ContextKeys.LOCATION, location); + ActionManager.trigger(context, config.breakActions()); + world.removeBlockState(pos3); } @@ -297,6 +304,7 @@ protected boolean tryWateringPot(Player player, Context context, CustomC } public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, PotConfig potConfig, String blockID) { + if (potConfig == null) return null; Optional optionalPotState = world.getBlockState(pos3); if (optionalPotState.isPresent()) { CustomCropsBlockState potState = optionalPotState.get(); From 3a2b8c9e1fc9b2e63e82092009b0579af3b4f5a4 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Tue, 3 Sep 2024 01:46:24 +0800 Subject: [PATCH 075/329] Fixed dead crop not removed --- .../net/momirealms/customcrops/api/core/block/PotBlock.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 01120f593..59c4d9942 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -163,6 +163,9 @@ public void onBreak(WrappedBreakEvent event) { world.removeBlockState(pos3.add(0,1,0)); BukkitCustomCropsPlugin.getInstance().getItemManager().remove(upperLocation, ExistenceForm.ANY); } + if (cropConfig == null && Registries.ITEM_TO_DEAD_CROP.containsKey(upperID)) { + BukkitCustomCropsPlugin.getInstance().getItemManager().remove(upperLocation, ExistenceForm.ANY); + } context.arg(ContextKeys.LOCATION, location); ActionManager.trigger(context, config.breakActions()); From 31976acc2aaa49cd816a68131a2bafbc9f4c49cb Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:05:13 +0800 Subject: [PATCH 076/329] rearrange api structure --- .../api/action/AbstractActionManager.java | 7 +- .../api/core/AbstractCustomEventListener.java | 7 +- .../customcrops/api/core/ConfigManager.java | 13 ++-- .../customcrops/api/core/Registries.java | 12 ++-- .../customcrops/api/core/RegistryAccess.java | 2 +- .../api/core/SimpleRegistryAccess.java | 2 +- .../customcrops/api/core/block/CropBlock.java | 6 +- .../customcrops/api/core/block/DeadCrop.java | 1 + .../customcrops/api/core/block/PotBlock.java | 7 +- .../api/core/block/SprinklerBlock.java | 2 + .../api/core/item/FertilizerItem.java | 6 +- .../customcrops/api/core/item/SeedItem.java | 6 +- .../api/core/item/SprinklerItem.java | 2 +- .../api/core/item/WateringCanItem.java | 4 ++ .../{block => mechanic/crop}/BoneMeal.java | 2 +- .../{block => mechanic/crop}/CropConfig.java | 2 +- .../crop}/CropConfigImpl.java | 2 +- .../crop}/CropStageConfig.java | 2 +- .../crop}/CropStageConfigImpl.java | 2 +- .../{block => mechanic/crop}/CrowAttack.java | 2 +- .../crop}/DeathCondition.java | 2 +- .../crop}/GrowCondition.java | 2 +- .../crop}/VariationData.java | 2 +- .../fertilizer}/AbstractFertilizerConfig.java | 2 +- .../fertilizer}/Fertilizer.java | 2 +- .../fertilizer}/FertilizerConfig.java | 2 +- .../fertilizer}/FertilizerImpl.java | 2 +- .../fertilizer}/FertilizerType.java | 2 +- .../fertilizer}/Quality.java | 2 +- .../fertilizer}/QualityImpl.java | 2 +- .../fertilizer}/SoilRetain.java | 2 +- .../fertilizer}/SoilRetainImpl.java | 2 +- .../fertilizer}/SpeedGrow.java | 2 +- .../fertilizer}/SpeedGrowImpl.java | 2 +- .../fertilizer}/Variation.java | 2 +- .../fertilizer}/VariationImpl.java | 2 +- .../fertilizer}/YieldIncrease.java | 2 +- .../fertilizer}/YieldIncreaseImpl.java | 2 +- .../{block => mechanic/pot}/PotConfig.java | 4 +- .../pot}/PotConfigImpl.java | 4 +- .../sprinkler}/SprinklerConfig.java | 2 +- .../sprinkler}/SprinklerConfigImpl.java | 2 +- .../wateringcan}/WateringCanConfig.java | 2 +- .../wateringcan}/WateringCanConfigImpl.java | 2 +- .../api/event/BoneMealDispenseEvent.java | 65 ++++++++++++++---- .../api/event/BoneMealUseEvent.java | 67 +++++++++++++++--- .../customcrops/api/event/CropBreakEvent.java | 2 +- .../api/event/CropInteractEvent.java | 65 ++++++++++++++++-- .../customcrops/api/event/CropPlantEvent.java | 68 +++++++++++++++---- .../api/event/CustomCropsReloadEvent.java | 23 +++++++ .../api/event/FertilizerUseEvent.java | 64 ++++++++++++++--- .../api/event/GreenhouseGlassBreakEvent.java | 61 ++++++++++++++++- .../event/GreenhouseGlassInteractEvent.java | 55 +++++++++++++-- .../api/event/GreenhouseGlassPlaceEvent.java | 44 +++++++++++- .../customcrops/api/event/PotBreakEvent.java | 68 +++++++++++++++++-- .../customcrops/api/event/PotFillEvent.java | 63 +++++++++++++++-- .../api/event/PotInteractEvent.java | 58 +++++++++++++--- .../customcrops/api/event/PotPlaceEvent.java | 58 ++++++++++++++-- .../api/event/ScarecrowBreakEvent.java | 61 ++++++++++++++++- .../api/event/ScarecrowInteractEvent.java | 55 +++++++++++++-- .../api/event/ScarecrowPlaceEvent.java | 44 +++++++++++- .../api/event/SprinklerBreakEvent.java | 63 +++++++++++++++-- .../api/event/SprinklerFillEvent.java | 63 +++++++++++++++-- .../api/event/SprinklerInteractEvent.java | 57 ++++++++++++++-- .../api/event/SprinklerPlaceEvent.java | 58 ++++++++++++++-- .../api/event/WateringCanFillEvent.java | 56 ++++++++++++--- .../api/event/WateringCanWaterPotEvent.java | 60 ++++++++++++++-- .../event/WateringCanWaterSprinklerEvent.java | 66 ++++++++++++++++-- .../AbstractRequirementManager.java | 6 +- .../bukkit/BukkitCustomCropsPluginImpl.java | 1 + .../bukkit/action/BlockActionManager.java | 6 +- .../bukkit/action/PlayerActionManager.java | 2 +- .../bukkit/config/BukkitConfigManager.java | 10 ++- .../customcrops/bukkit/config/ConfigType.java | 14 ++-- 74 files changed, 1311 insertions(+), 213 deletions(-) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/BoneMeal.java (97%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/CropConfig.java (98%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/CropConfigImpl.java (99%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/CropStageConfig.java (97%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/CropStageConfigImpl.java (98%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/CrowAttack.java (98%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/DeathCondition.java (97%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/GrowCondition.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/crop}/VariationData.java (95%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/AbstractFertilizerConfig.java (98%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/Fertilizer.java (95%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/FertilizerConfig.java (95%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/FertilizerImpl.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/FertilizerType.java (99%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/Quality.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/QualityImpl.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/SoilRetain.java (95%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/SoilRetainImpl.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/SpeedGrow.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/SpeedGrowImpl.java (97%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/Variation.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/VariationImpl.java (97%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/YieldIncrease.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/fertilizer}/YieldIncreaseImpl.java (97%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/pot}/PotConfig.java (96%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/pot}/PotConfigImpl.java (98%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/sprinkler}/SprinklerConfig.java (98%) rename api/src/main/java/net/momirealms/customcrops/api/core/{block => mechanic/sprinkler}/SprinklerConfigImpl.java (99%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/wateringcan}/WateringCanConfig.java (97%) rename api/src/main/java/net/momirealms/customcrops/api/core/{item => mechanic/wateringcan}/WateringCanConfigImpl.java (99%) diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java index e808a586e..da5f84c90 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java @@ -25,8 +25,11 @@ import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.block.*; -import net.momirealms.customcrops.api.core.item.Fertilizer; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +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.CustomCropsChunk; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java index d1f5309df..b42624e2e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -22,6 +22,11 @@ import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.block.*; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; @@ -312,7 +317,7 @@ public void onDispenser(BlockDispenseEvent event) { if (point < cropConfig.maxPoints()) { for (BoneMeal boneMeal : cropConfig.boneMeals()) { if (boneMeal.isDispenserAllowed()) { - if (EventUtils.fireAndCheckCancel(new BoneMealDispenseEvent(block, itemStack, location, boneMeal, state))) { + if (EventUtils.fireAndCheckCancel(new BoneMealDispenseEvent(block, location, state, itemStack, boneMeal))) { event.setCancelled(true); return; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java index 7d9d4fd3c..e5189c90f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -12,10 +12,15 @@ import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; import dev.dejvokep.boostedyaml.utils.format.NodeRole; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; -import net.momirealms.customcrops.api.core.block.*; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; -import net.momirealms.customcrops.api.core.item.FertilizerType; -import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.DeathCondition; +import net.momirealms.customcrops.api.core.mechanic.crop.GrowCondition; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.misc.water.WateringMethod; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java index 7a2f19226..4745248d8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java @@ -17,14 +17,14 @@ package net.momirealms.customcrops.api.core; -import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; -import net.momirealms.customcrops.api.core.block.PotConfig; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.item.CustomCropsItem; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; -import net.momirealms.customcrops.api.core.item.FertilizerType; -import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.common.annotation.DoNotUse; import net.momirealms.customcrops.common.util.Key; import org.jetbrains.annotations.ApiStatus; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java index 875692e95..ef679f38c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java @@ -19,7 +19,7 @@ import net.momirealms.customcrops.api.core.block.CustomCropsBlock; import net.momirealms.customcrops.api.core.item.CustomCropsItem; -import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; import net.momirealms.customcrops.common.util.Key; public interface RegistryAccess { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java index b3a94e4e6..c01e5bdde 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java @@ -20,7 +20,7 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; import net.momirealms.customcrops.api.core.item.CustomCropsItem; -import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; import net.momirealms.customcrops.common.util.Key; public class SimpleRegistryAccess implements RegistryAccess { 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 3f474b447..0f2852f62 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 @@ -23,8 +23,10 @@ import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.item.Fertilizer; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.*; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java index 9d2bdda08..ff363257c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java @@ -22,6 +22,7 @@ import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; import net.momirealms.customcrops.api.core.Registries; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 59c4d9942..ad4a41066 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -23,8 +23,11 @@ import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.item.Fertilizer; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +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; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java index dd6baac07..e91db6639 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -24,6 +24,8 @@ import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java index d50f8f88a..cfd90bb10 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java @@ -26,9 +26,11 @@ import net.momirealms.customcrops.api.core.InteractionResult; import net.momirealms.customcrops.api.core.Registries; import net.momirealms.customcrops.api.core.block.CropBlock; -import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.block.PotBlock; -import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +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; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java index ba130021a..a4dc56980 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java @@ -23,9 +23,9 @@ import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.block.CropBlock; -import net.momirealms.customcrops.api.core.block.CropConfig; -import net.momirealms.customcrops.api.core.block.CropStageConfig; -import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java index e0cc68a89..8ed8fac0d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java @@ -23,7 +23,7 @@ import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.block.SprinklerBlock; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java index aaa19539c..08c1a211c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java @@ -25,6 +25,10 @@ import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.block.*; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/BoneMeal.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/BoneMeal.java index d74f05fb8..43fa2444c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/BoneMeal.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/BoneMeal.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.action.ActionManager; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfig.java similarity index 98% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfig.java index 08ee3aa22..c8f409abb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfig.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfigImpl.java similarity index 99% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfigImpl.java index e0862ecb6..d62ba3203 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfigImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import com.google.common.base.Preconditions; import net.momirealms.customcrops.api.action.Action; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfig.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfig.java index 0ec236cfe..3639c54bf 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfig.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfigImpl.java similarity index 98% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfigImpl.java index 8a2f1d713..80f0f1f81 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropStageConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfigImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CrowAttack.java similarity index 98% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CrowAttack.java index 51d571cfa..4da1a2ffa 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CrowAttack.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CrowAttack.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.util.LocationUtils; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/DeathCondition.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/DeathCondition.java index cfedff1d2..ebcdeec5d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeathCondition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/DeathCondition.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.core.ExistenceForm; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/GrowCondition.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/GrowCondition.java index c03b598b8..a3277ed51 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/GrowCondition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/GrowCondition.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/VariationData.java similarity index 95% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/VariationData.java index 8afd76364..4d1d3ab6e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/VariationData.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/VariationData.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.crop; import net.momirealms.customcrops.api.core.ExistenceForm; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/AbstractFertilizerConfig.java similarity index 98% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/AbstractFertilizerConfig.java index 1f930b9e4..759214f06 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/AbstractFertilizerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/AbstractFertilizerConfig.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Fertilizer.java similarity index 95% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Fertilizer.java index 057729249..e17da6d77 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/Fertilizer.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Fertilizer.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.core.Registries; import org.jetbrains.annotations.Nullable; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerConfig.java similarity index 95% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerConfig.java index 6919c43e4..affce2119 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerConfig.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerImpl.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerImpl.java index 53013acab..2f2ca8484 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; public class FertilizerImpl implements Fertilizer { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerType.java similarity index 99% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerType.java index 0868408b1..faa9acbae 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerType.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerType.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import dev.dejvokep.boostedyaml.block.implementation.Section; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Quality.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Quality.java index 80db0ff95..39bac8c7d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/Quality.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Quality.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/QualityImpl.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/QualityImpl.java index 3895ed887..92c656bca 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/QualityImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/QualityImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SoilRetain.java similarity index 95% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SoilRetain.java index 36d045f3c..b6baac27d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetain.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SoilRetain.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SoilRetainImpl.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SoilRetainImpl.java index 8a923b894..d50788ea5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SoilRetainImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SoilRetainImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SpeedGrow.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SpeedGrow.java index 416898e2c..e00300a69 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrow.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SpeedGrow.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SpeedGrowImpl.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SpeedGrowImpl.java index 48f1c2ad8..e314d8865 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SpeedGrowImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/SpeedGrowImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Variation.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Variation.java index c57482eb0..249ecb60d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/Variation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Variation.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/VariationImpl.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/VariationImpl.java index 5d2b850c6..2caa72be3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/VariationImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/VariationImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/YieldIncrease.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/YieldIncrease.java index f455f503b..7bb75d38f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncrease.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/YieldIncrease.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/YieldIncreaseImpl.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/YieldIncreaseImpl.java index 867946715..c7a9483ac 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/YieldIncreaseImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/YieldIncreaseImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.fertilizer; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.requirement.Requirement; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfig.java similarity index 96% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfig.java index cc804f526..2fd0794bf 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfig.java @@ -15,10 +15,10 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.pot; import net.momirealms.customcrops.api.action.Action; -import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.misc.water.WateringMethod; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfigImpl.java similarity index 98% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfigImpl.java index b203610d3..595846470 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfigImpl.java @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.pot; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; -import net.momirealms.customcrops.api.core.item.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.WaterBar; import net.momirealms.customcrops.api.misc.water.WateringMethod; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfig.java similarity index 98% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfig.java index 5f9b30fbb..52195b183 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfig.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.sprinkler; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfigImpl.java similarity index 99% rename from api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfigImpl.java index 5fa046886..0a4d1bcc5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfigImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.block; +package net.momirealms.customcrops.api.core.mechanic.sprinkler; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.core.ExistenceForm; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfig.java similarity index 97% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfig.java index 875b9aabc..ba51753f7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfig.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.wateringcan; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.misc.value.TextValue; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfigImpl.java similarity index 99% rename from api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfigImpl.java index 06fc187aa..59fd7f7f4 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfigImpl.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.core.item; +package net.momirealms.customcrops.api.core.mechanic.wateringcan; import net.momirealms.customcrops.api.action.Action; import net.momirealms.customcrops.api.misc.value.TextValue; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java index 9ea3a5541..0a94d92d4 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealDispenseEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.BoneMeal; +import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.block.Block; @@ -28,7 +28,9 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when the bone meal is dispensed + * The BoneMealDispenseEvent class represents an event that is triggered when a dispenser + * applies bone meal to a custom crop block in the CustomCrops plugin. This event allows + * for handling the action of bone meal being dispensed and applied to crops. */ public class BoneMealDispenseEvent extends Event implements Cancellable { @@ -40,12 +42,21 @@ public class BoneMealDispenseEvent extends Event implements Cancellable { private final ItemStack boneMealItem; private final Block dispenser; + /** + * Constructor for the BoneMealDispenseEvent. + * + * @param dispenser The dispenser block that dispensed the bone meal. + * @param location The location of the crop block affected by the bone meal. + * @param blockState The state of the crop block before the bone meal is applied. + * @param boneMealItem The ItemStack representing the bone meal item used. + * @param boneMeal The BoneMeal configuration being used. + */ public BoneMealDispenseEvent( @NotNull Block dispenser, - @NotNull ItemStack boneMealItem, @NotNull Location location, - @NotNull BoneMeal boneMeal, - @NotNull CustomCropsBlockState blockState + @NotNull CustomCropsBlockState blockState, + @NotNull ItemStack boneMealItem, + @NotNull BoneMeal boneMeal ) { this.location = location; this.blockState = blockState; @@ -54,21 +65,41 @@ public BoneMealDispenseEvent( this.dispenser = dispenser; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -76,8 +107,9 @@ public HandlerList getHandlers() { } /** - * Get the crop location - * @return location + * Gets the location of the crop block affected by the bone meal. + * + * @return the location of the crop block. */ @NotNull public Location getLocation() { @@ -85,24 +117,29 @@ public Location getLocation() { } /** - * Get the item in player's hand - * If there's nothing in hand, it would return AIR - * @return item in hand + * Gets the ItemStack representing the bone meal item used. + * + * @return the ItemStack of bone meal. */ @NotNull public ItemStack getBoneMealItem() { return boneMealItem; } + /** + * Gets the state of the crop block before the bone meal was applied. + * + * @return the block state of the crop. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } /** - * Get the bone meal's config + * Gets the configuration of the bone meal being used. * - * @return bone meal config + * @return the BoneMeal configuration. */ @NotNull public BoneMeal getBoneMeal() { @@ -110,9 +147,9 @@ public BoneMeal getBoneMeal() { } /** - * Get the dispenser block + * Gets the dispenser block that dispensed the bone meal. * - * @return dispenser block + * @return the dispenser block. */ @NotNull public Block getDispenser() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java index 37e401122..883e8b60d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/BoneMealUseEvent.java @@ -17,8 +17,8 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.BoneMeal; -import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -30,7 +30,8 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when a player interacts a crop with a bone meal + * The BoneMealUseEvent class represents an event triggered when a player uses bone meal + * on a custom crop block in the CustomCrops plugin. */ public class BoneMealUseEvent extends PlayerEvent implements Cancellable { @@ -43,6 +44,17 @@ public class BoneMealUseEvent extends PlayerEvent implements Cancellable { private final EquipmentSlot equipmentSlot; private final CropConfig config; + /** + * Constructor for the BoneMealUseEvent. + * + * @param player The player who used the bone meal. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param location The location of the crop block affected by the bone meal. + * @param boneMeal The BoneMeal configuration being applied. + * @param blockState The state of the crop block before the bone meal is applied. + * @param equipmentSlot The equipment slot where the bone meal item is held. + * @param config The crop configuration associated with the block being targeted. + */ public BoneMealUseEvent( @NotNull Player player, @NotNull ItemStack itemInHand, @@ -61,21 +73,41 @@ public BoneMealUseEvent( this.config = config; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -83,45 +115,60 @@ public HandlerList getHandlers() { } /** - * Get the crop location + * Gets the location of the crop block affected by the bone meal. * - * @return location + * @return the location of the crop block. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the crop configuration associated with the block being targeted. + * + * @return the crop configuration. + */ @NotNull public CropConfig getConfig() { return config; } /** - * Get the item in player's hand - * If there's nothing in hand, it would return AIR + * Gets the ItemStack representing the item in the player's hand. + * If there is nothing in hand, it would return AIR. * - * @return item in hand + * @return the ItemStack in hand. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the state of the crop block before the bone meal was applied. + * + * @return the block state of the crop. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the equipment slot where the bone meal item is held. + * + * @return the equipment slot. + */ @NotNull public EquipmentSlot getEquipmentSlot() { return equipmentSlot; } /** - * Get the bone meal config + * Gets the configuration of the bone meal being used. * - * @return bone meal config + * @return the BoneMeal configuration. */ @NotNull public BoneMeal getBoneMeal() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java index 1d2daefd7..5ef511714 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java @@ -18,7 +18,7 @@ package net.momirealms.customcrops.api.event; import net.momirealms.customcrops.api.core.block.BreakReason; -import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.block.Block; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java index 636ec03ea..280961c27 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -29,7 +29,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when a player interacts a crop + * An event that is triggered when a player interacts with a crop in the CustomCrops plugin. */ public class CropInteractEvent extends PlayerEvent implements Cancellable { @@ -42,6 +42,17 @@ public class CropInteractEvent extends PlayerEvent implements Cancellable { private final CropConfig config; private final String stageItemID; + /** + * Constructor for the CropInteractEvent. + * + * @param who The player who interacted with the crop. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param location The location of the crop block being interacted with. + * @param blockState The state of the crop block before interaction. + * @param hand The hand (main or offhand) used by the player. + * @param config The crop configuration associated with the crop block. + * @param stageItemID The ID representing the stage of the crop. + */ public CropInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @@ -60,21 +71,41 @@ public CropInteractEvent( this.stageItemID = stageItemID; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -82,9 +113,9 @@ public HandlerList getHandlers() { } /** - * Get the crop location + * Gets the location of the crop block being interacted with. * - * @return location + * @return the location of the crop block. */ @NotNull public Location getLocation() { @@ -92,31 +123,51 @@ public Location getLocation() { } /** - * Get the item in player's hand - * If there's nothing in hand, it would return AIR + * Gets the ItemStack representing the item in the player's hand. + * If there is nothing in hand, it would return AIR. * - * @return item in hand + * @return the ItemStack in hand. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the crop configuration associated with the crop block. + * + * @return the crop configuration. + */ @NotNull public CropConfig getCropConfig() { return config; } + /** + * Gets the state of the crop block before the interaction occurred. + * + * @return the block state of the crop. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the hand (main or offhand) used by the player to interact with the crop. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } + /** + * Gets the ID representing the stage of the crop. + * + * @return the stage item ID. + */ @NotNull public String getStageItemID() { return stageItemID; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java index 98379b697..5750b4d98 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -29,7 +29,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when planting a crop + * An event that is triggered when a player plants a crop in the CustomCrops plugin. */ public class CropPlantEvent extends PlayerEvent implements Cancellable { @@ -42,6 +42,17 @@ public class CropPlantEvent extends PlayerEvent implements Cancellable { private final EquipmentSlot hand; private int point; + /** + * Constructor for the CropPlantEvent. + * + * @param who The player who is planting the crop. + * @param itemInHand The ItemStack representing the item in the player's hand used for planting. + * @param hand The hand (main or offhand) used by the player for planting. + * @param location The location where the crop is being planted. + * @param config The crop configuration associated with the crop being planted. + * @param blockState The state of the block where the crop is planted. + * @param point The initial point value associated with planting the crop. + */ public CropPlantEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @@ -60,51 +71,81 @@ public CropPlantEvent( this.blockState = blockState; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } /** - * Get the seed item + * Gets the ItemStack representing the seed item in the player's hand. * - * @return seed item + * @return the seed item. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { return getHandlerList(); } + /** + * Gets the state of the block where the crop is planted. + * + * @return the block state of the crop. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the hand (main or offhand) used by the player to plant the crop. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } /** - * Get the crop's config + * Gets the crop configuration associated with the crop being planted. * - * @return crop + * @return the crop configuration. */ @NotNull public CropConfig getCropConfig() { @@ -112,9 +153,9 @@ public CropConfig getCropConfig() { } /** - * Get the crop's location + * Gets the location where the crop is being planted. * - * @return location + * @return the location of the crop. */ @NotNull public Location getLocation() { @@ -122,18 +163,19 @@ public Location getLocation() { } /** - * Get the initial point - * It would be 0 when planting + * Gets the initial point value associated with planting the crop. + * This value is typically 0 when the crop is first planted. * - * @return point + * @return the initial point. */ public int getPoint() { return point; } /** - * Set the initial point - * @param point point + * Sets the initial point value associated with planting the crop. + * + * @param point the new initial point value. */ public void setPoint(int point) { this.point = point; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java index 8aa810b68..b37d6a07d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java @@ -22,25 +22,48 @@ import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; +/** + * An event that is triggered when the CustomCrops plugin is reloaded. + */ public class CustomCropsReloadEvent extends Event { private static final HandlerList handlerList = new HandlerList(); private final BukkitCustomCropsPlugin plugin; + /** + * Constructor for the CustomCropsReloadEvent. + * + * @param plugin The instance of the CustomCrops plugin being reloaded. + */ public CustomCropsReloadEvent(BukkitCustomCropsPlugin plugin) { this.plugin = plugin; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ public static HandlerList getHandlerList() { return handlerList; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { return getHandlerList(); } + /** + * Gets the instance of the CustomCrops plugin that is being reloaded. + * + * @return the plugin instance. + */ public BukkitCustomCropsPlugin getPluginInstance() { return plugin; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java index 25b37926f..32212e06b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java @@ -17,8 +17,8 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.PotConfig; -import net.momirealms.customcrops.api.core.item.Fertilizer; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -30,7 +30,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when player tries adding fertilizer to pot + * An event that is triggered when a player tries to add fertilizer to a pot in the CustomCrops plugin. */ public class FertilizerUseEvent extends PlayerEvent implements Cancellable { @@ -43,6 +43,17 @@ public class FertilizerUseEvent extends PlayerEvent implements Cancellable { private final EquipmentSlot hand; private final PotConfig config; + /** + * Constructor for the FertilizerUseEvent. + * + * @param player The player who is attempting to add fertilizer. + * @param itemInHand The ItemStack representing the fertilizer item in the player's hand. + * @param fertilizer The Fertilizer configuration being applied. + * @param location The location of the pot where the fertilizer is being added. + * @param blockState The state of the block (pot) before the fertilizer is added. + * @param hand The hand (main or offhand) used by the player to apply the fertilizer. + * @param config The pot configuration associated with the pot being fertilized. + */ public FertilizerUseEvent( @NotNull Player player, @NotNull ItemStack itemInHand, @@ -62,30 +73,50 @@ public FertilizerUseEvent( this.config = config; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { cancelled = cancel; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @Override public @NotNull HandlerList getHandlers() { return handlers; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } /** - * Get the fertilizer item in hand + * Gets the ItemStack representing the fertilizer item in the player's hand. * - * @return item in hand + * @return the item in hand. */ @NotNull public ItemStack getItemInHand() { @@ -93,34 +124,49 @@ public ItemStack getItemInHand() { } /** - * Get the pot's location + * Gets the location of the pot where the fertilizer is being added. * - * @return location + * @return the location of the pot. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the state of the block (pot) before the fertilizer is added. + * + * @return the block state of the pot. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the hand (main or offhand) used by the player to apply the fertilizer. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } + /** + * Gets the pot configuration associated with the pot being fertilized. + * + * @return the pot configuration. + */ @NotNull public PotConfig getPotConfig() { return config; } /** - * Get the fertilizer's config + * Gets the fertilizer being applied. * - * @return fertilizer config + * @return the fertilizer configuration. */ @NotNull public Fertilizer getFertilizer() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java index b14a3a2c0..024856b9b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassBreakEvent.java @@ -29,7 +29,7 @@ import org.jetbrains.annotations.Nullable; /** - * An event that triggered when breaking greenhouse glass + * An event that is triggered when a greenhouse glass block is broken in the CustomCrops plugin. */ public class GreenhouseGlassBreakEvent extends Event implements Cancellable { @@ -42,6 +42,16 @@ public class GreenhouseGlassBreakEvent extends Event implements Cancellable { private final CustomCropsBlockState blockState; private final String glassItemID; + /** + * Constructor for the GreenhouseGlassBreakEvent. + * + * @param entityBreaker The entity that caused the glass to break, if applicable (can be null). + * @param blockBreaker The block that caused the glass to break, if applicable (can be null). + * @param location The location of the greenhouse glass block being broken. + * @param glassItemID The item ID representing the glass type being broken. + * @param blockState The state of the greenhouse glass block before it was broken. + * @param reason The reason why the glass was broken. + */ public GreenhouseGlassBreakEvent( @Nullable Entity entityBreaker, @Nullable Block blockBreaker, @@ -58,21 +68,41 @@ public GreenhouseGlassBreakEvent( this.glassItemID = glassItemID; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -80,35 +110,60 @@ public HandlerList getHandlers() { } /** - * Get the glass location + * Gets the location of the greenhouse glass block being broken. * - * @return location + * @return the location of the glass block. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the entity responsible for breaking the glass, if applicable. + * + * @return the entity that caused the break, or null if not applicable. + */ @Nullable public Entity getEntityBreaker() { return entityBreaker; } + /** + * Gets the block responsible for breaking the glass, if applicable. + * + * @return the block that caused the break, or null if not applicable. + */ @Nullable public Block getBlockBreaker() { return blockBreaker; } + /** + * Gets the reason for the greenhouse glass breakage. + * + * @return the reason for the break. + */ @NotNull public BreakReason getReason() { return reason; } + /** + * Gets the state of the greenhouse glass block before it was broken. + * + * @return the block state of the glass. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the item ID representing the glass type being broken. + * + * @return the glass item ID. + */ public String getGlassItemID() { return glassItemID; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java index c8886effc..6c332c428 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassInteractEvent.java @@ -28,7 +28,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when interacting a sprinkler + * An event that is triggered when a player interacts with a greenhouse glass block in the CustomCrops plugin. */ public class GreenhouseGlassInteractEvent extends PlayerEvent implements Cancellable { @@ -40,6 +40,16 @@ public class GreenhouseGlassInteractEvent extends PlayerEvent implements Cancell private final String glassItemID; private final EquipmentSlot hand; + /** + * Constructor for the GreenhouseGlassInteractEvent. + * + * @param who The player who is interacting with the greenhouse glass. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param location The location of the greenhouse glass block being interacted with. + * @param glassItemID The item ID representing the glass type being interacted with. + * @param blockState The state of the greenhouse glass block at the time of interaction. + * @param hand The hand (main or offhand) used by the player for the interaction. + */ public GreenhouseGlassInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @@ -56,21 +66,41 @@ public GreenhouseGlassInteractEvent( this.glassItemID = glassItemID; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -78,34 +108,49 @@ public HandlerList getHandlers() { } /** - * Get the sprinkler location + * Gets the location of the greenhouse glass block being interacted with. * - * @return location + * @return the location of the glass block. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the state of the greenhouse glass block at the time of interaction. + * + * @return the block state of the glass. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the hand (main or offhand) used by the player to interact with the glass. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } + /** + * Gets the item ID representing the glass type being interacted with. + * + * @return the glass item ID. + */ @NotNull public String getGlassItemID() { return glassItemID; } /** - * Get the item in player's hand + * Gets the ItemStack representing the item in the player's hand. * - * @return item in hand + * @return the item in hand. */ @NotNull public ItemStack getItemInHand() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java index b77b30659..b2997f828 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/GreenhouseGlassPlaceEvent.java @@ -26,7 +26,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when placing greenhouse glass + * An event that is triggered when a player places a greenhouse glass block in the CustomCrops plugin. */ public class GreenhouseGlassPlaceEvent extends PlayerEvent implements Cancellable { @@ -36,6 +36,14 @@ public class GreenhouseGlassPlaceEvent extends PlayerEvent implements Cancellabl private final CustomCropsBlockState blockState; private final String glassItemID; + /** + * Constructor for the GreenhouseGlassPlaceEvent. + * + * @param who The player who is placing the greenhouse glass block. + * @param location The location where the greenhouse glass block is being placed. + * @param glassItemID The item ID representing the glass type being placed. + * @param blockState The state of the block where the greenhouse glass is placed. + */ public GreenhouseGlassPlaceEvent( @NotNull Player who, @NotNull Location location, @@ -48,21 +56,41 @@ public GreenhouseGlassPlaceEvent( this.blockState = blockState; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -70,20 +98,30 @@ public HandlerList getHandlers() { } /** - * Get the glass location + * Gets the location where the greenhouse glass block is being placed. * - * @return location + * @return the location of the glass block. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the state of the block where the greenhouse glass is placed. + * + * @return the block state of the glass. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the item ID representing the glass type being placed. + * + * @return the glass item ID. + */ @NotNull public String getGlassItemID() { return glassItemID; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java index 1bf00d06e..e3d1060a2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java @@ -18,7 +18,7 @@ package net.momirealms.customcrops.api.event; import net.momirealms.customcrops.api.core.block.BreakReason; -import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.block.Block; @@ -31,7 +31,7 @@ import org.jetbrains.annotations.Nullable; /** - * An event that triggered when breaking a pot + * An event that is triggered when a pot block is broken in the CustomCrops plugin. */ public class PotBreakEvent extends Event implements Cancellable { @@ -44,6 +44,16 @@ public class PotBreakEvent extends Event implements Cancellable { private final Block blockBreaker; private final BreakReason reason; + /** + * Constructor for the PotBreakEvent. + * + * @param entityBreaker The entity that caused the pot to break, if applicable (can be null). + * @param blockBreaker The block that caused the pot to break, if applicable (can be null). + * @param location The location of the pot block being broken. + * @param config The configuration of the pot that is being broken. + * @param blockState The state of the pot block before it was broken. + * @param reason The reason why the pot was broken. + */ public PotBreakEvent( @Nullable Entity entityBreaker, @Nullable Block blockBreaker, @@ -60,21 +70,41 @@ public PotBreakEvent( this.config = config; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -82,20 +112,30 @@ public HandlerList getHandlers() { } /** - * Get the pot location + * Gets the location of the pot block being broken. * - * @return location + * @return the location of the pot block. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the entity responsible for breaking the pot, if applicable. + * + * @return the entity that caused the break, or null if not applicable. + */ @Nullable public Entity getEntityBreaker() { return entityBreaker; } + /** + * Gets the player responsible for breaking the pot, if the breaker is a player. + * + * @return the player that caused the break, or null if not applicable. + */ @Nullable public Player getPlayer() { if (entityBreaker instanceof Player player) { @@ -104,21 +144,41 @@ public Player getPlayer() { return null; } + /** + * Gets the reason for the pot breakage. + * + * @return the reason for the break. + */ @NotNull public BreakReason getReason() { return reason; } + /** + * Gets the configuration of the pot that is being broken. + * + * @return the pot configuration. + */ @NotNull public PotConfig getPotConfig() { return config; } + /** + * Gets the state of the pot block before it was broken. + * + * @return the block state of the pot. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the block responsible for breaking the pot, if applicable. + * + * @return the block that caused the break, or null if not applicable. + */ @Nullable public Block getBlockBreaker() { return blockBreaker; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java index 88dc70c5b..9f6cbd01d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotFillEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.WateringMethod; import org.bukkit.Location; @@ -30,7 +30,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when a pot is watered by the fill-methods set in each pot's config + * An event that is triggered when a pot is watered using the fill methods specified in each pot's configuration. */ public class PotFillEvent extends PlayerEvent implements Cancellable { @@ -43,6 +43,17 @@ public class PotFillEvent extends PlayerEvent implements Cancellable { private final CustomCropsBlockState blockState; private final EquipmentSlot hand; + /** + * Constructor for the PotFillEvent. + * + * @param player The player who is watering the pot. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param hand The hand (main or offhand) used by the player for watering. + * @param location The location of the pot being watered. + * @param wateringMethod The method used to water the pot. + * @param blockState The state of the pot block before it is watered. + * @param config The configuration of the pot being watered. + */ public PotFillEvent( @NotNull Player player, @NotNull ItemStack itemInHand, @@ -61,21 +72,41 @@ public PotFillEvent( this.hand = hand; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -83,9 +114,9 @@ public HandlerList getHandlers() { } /** - * Get the pot location + * Gets the location of the pot being watered. * - * @return location + * @return the location of the pot. */ @NotNull public Location getLocation() { @@ -93,30 +124,50 @@ public Location getLocation() { } /** - * Get the item in hand + * Gets the ItemStack representing the item in the player's hand. * - * @return item in hand + * @return the item in hand. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the configuration of the pot being watered. + * + * @return the pot configuration. + */ @NotNull public PotConfig getPotConfig() { return config; } + /** + * Gets the method used to water the pot. + * + * @return the watering method. + */ @NotNull public WateringMethod getWateringMethod() { return wateringMethod; } + /** + * Gets the state of the pot block before it is watered. + * + * @return the block state of the pot. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the hand (main or offhand) used by the player to water the pot. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java index ebc2796e8..2d0d7fd15 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -29,7 +29,7 @@ import org.jetbrains.annotations.NotNull; /** - * This event is called when a player is interacting a pot + * This event is called when a player interacts with a pot in the CustomCrops plugin. */ public class PotInteractEvent extends PlayerEvent implements Cancellable { @@ -41,6 +41,16 @@ public class PotInteractEvent extends PlayerEvent implements Cancellable { private final PotConfig config; private final EquipmentSlot hand; + /** + * Constructor for the PotInteractEvent. + * + * @param who The player who is interacting with the pot. + * @param hand The hand (main or offhand) used by the player for the interaction. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param config The configuration of the pot being interacted with. + * @param location The location of the pot block being interacted with. + * @param blockState The state of the pot block at the time of interaction. + */ public PotInteractEvent( @NotNull Player who, @NotNull EquipmentSlot hand, @@ -57,21 +67,41 @@ public PotInteractEvent( this.hand = hand; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -79,25 +109,30 @@ public HandlerList getHandlers() { } /** - * Get the item in player's hand - * If there's nothing in hand, it would return AIR + * Gets the ItemStack representing the item in the player's hand. + * If there is nothing in hand, it would return AIR. * - * @return item in hand + * @return the item in hand. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the hand (main or offhand) used by the player to interact with the pot. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } /** - * Get the pot location + * Gets the location of the pot block being interacted with. * - * @return pot location + * @return the location of the pot. */ @NotNull public Location getLocation() { @@ -105,15 +140,20 @@ public Location getLocation() { } /** - * Get the pot's data + * Gets the state of the pot block at the time of interaction. * - * @return pot key + * @return the block state of the pot. */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the configuration of the pot being interacted with. + * + * @return the pot configuration. + */ @NotNull public PotConfig getPotConfig() { return config; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java index d10666735..91caaa9aa 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -29,7 +29,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when placing a pot + * An event that is triggered when a player places a pot in the CustomCrops plugin. */ public class PotPlaceEvent extends PlayerEvent implements Cancellable { @@ -41,6 +41,16 @@ public class PotPlaceEvent extends PlayerEvent implements Cancellable { private final ItemStack itemInHand; private final EquipmentSlot hand; + /** + * Constructor for the PotPlaceEvent. + * + * @param who The player who is placing the pot. + * @param location The location where the pot is being placed. + * @param config The configuration of the pot being placed. + * @param state The state of the block where the pot is placed. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param hand The hand (main or offhand) used by the player for placing the pot. + */ public PotPlaceEvent( @NotNull Player who, @NotNull Location location, @@ -57,21 +67,41 @@ public PotPlaceEvent( this.config = config; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -79,30 +109,50 @@ public HandlerList getHandlers() { } /** - * Get the pot location + * Gets the location where the pot is being placed. * - * @return location + * @return the location of the pot. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the state of the block where the pot is placed. + * + * @return the block state of the pot. + */ @NotNull public CustomCropsBlockState getState() { return state; } + /** + * Gets the ItemStack representing the item in the player's hand. + * + * @return the item in hand. + */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the hand (main or offhand) used by the player to place the pot. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } + /** + * Gets the configuration of the pot being placed. + * + * @return the pot configuration. + */ @NotNull public PotConfig getPotConfig() { return config; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java index 43a153b52..7efaa4d5d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowBreakEvent.java @@ -29,7 +29,7 @@ import org.jetbrains.annotations.Nullable; /** - * An event that triggered when breaking a scarecrow + * An event that is triggered when a scarecrow is broken in the CustomCrops plugin. */ public class ScarecrowBreakEvent extends Event implements Cancellable { @@ -42,6 +42,16 @@ public class ScarecrowBreakEvent extends Event implements Cancellable { private final CustomCropsBlockState blockState; private final String scarecrowItemID; + /** + * Constructor for the ScarecrowBreakEvent. + * + * @param entityBreaker The entity that caused the scarecrow to break, if applicable (can be null). + * @param blockBreaker The block that caused the scarecrow to break, if applicable (can be null). + * @param location The location of the scarecrow being broken. + * @param scarecrowItemID The item ID representing the scarecrow type being broken. + * @param blockState The state of the scarecrow block before it was broken. + * @param reason The reason why the scarecrow was broken. + */ public ScarecrowBreakEvent( @Nullable Entity entityBreaker, @Nullable Block blockBreaker, @@ -58,21 +68,41 @@ public ScarecrowBreakEvent( this.scarecrowItemID = scarecrowItemID; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -80,35 +110,60 @@ public HandlerList getHandlers() { } /** - * Get the scarecrow location + * Gets the location of the scarecrow being broken. * - * @return location + * @return the location of the scarecrow. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the entity responsible for breaking the scarecrow, if applicable. + * + * @return the entity that caused the break, or null if not applicable. + */ @Nullable public Entity getEntityBreaker() { return entityBreaker; } + /** + * Gets the block responsible for breaking the scarecrow, if applicable. + * + * @return the block that caused the break, or null if not applicable. + */ @Nullable public Block getBlockBreaker() { return blockBreaker; } + /** + * Gets the reason for the scarecrow breakage. + * + * @return the reason for the break. + */ @NotNull public BreakReason getReason() { return reason; } + /** + * Gets the state of the scarecrow block before it was broken. + * + * @return the block state of the scarecrow. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the item ID representing the scarecrow type being broken. + * + * @return the scarecrow item ID. + */ public String getScarecrowItemID() { return scarecrowItemID; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java index e8f7acd3d..7549eb93e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowInteractEvent.java @@ -28,7 +28,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when interacting a sprinkler + * An event that is triggered when a player interacts with a scarecrow in the CustomCrops plugin. */ public class ScarecrowInteractEvent extends PlayerEvent implements Cancellable { @@ -40,6 +40,16 @@ public class ScarecrowInteractEvent extends PlayerEvent implements Cancellable { private final String scarecrowItemID; private final EquipmentSlot hand; + /** + * Constructor for the ScarecrowInteractEvent. + * + * @param who The player who is interacting with the scarecrow. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param location The location of the scarecrow block being interacted with. + * @param scarecrowItemID The item ID representing the scarecrow type being interacted with. + * @param blockState The state of the scarecrow block at the time of interaction. + * @param hand The hand (main or offhand) used by the player for the interaction. + */ public ScarecrowInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @@ -56,21 +66,41 @@ public ScarecrowInteractEvent( this.scarecrowItemID = scarecrowItemID; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -78,34 +108,49 @@ public HandlerList getHandlers() { } /** - * Get the sprinkler location + * Gets the location of the scarecrow block being interacted with. * - * @return location + * @return the location of the scarecrow. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the state of the scarecrow block at the time of interaction. + * + * @return the block state of the scarecrow. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the hand (main or offhand) used by the player to interact with the scarecrow. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } + /** + * Gets the item ID representing the scarecrow type being interacted with. + * + * @return the scarecrow item ID. + */ @NotNull public String getScarecrowItemID() { return scarecrowItemID; } /** - * Get the item in player's hand + * Gets the ItemStack representing the item in the player's hand. * - * @return item in hand + * @return the item in hand. */ @NotNull public ItemStack getItemInHand() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java index 18f859d1b..484a8523e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/ScarecrowPlaceEvent.java @@ -26,7 +26,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when placing a scarecrow + * An event that is triggered when a player places a scarecrow in the CustomCrops plugin. */ public class ScarecrowPlaceEvent extends PlayerEvent implements Cancellable { @@ -36,6 +36,14 @@ public class ScarecrowPlaceEvent extends PlayerEvent implements Cancellable { private final String scarecrowItemID; private final CustomCropsBlockState blockState; + /** + * Constructor for the ScarecrowPlaceEvent. + * + * @param who The player who is placing the scarecrow. + * @param location The location where the scarecrow is being placed. + * @param scarecrowItemID The item ID representing the scarecrow type being placed. + * @param blockState The state of the block where the scarecrow is placed. + */ public ScarecrowPlaceEvent( @NotNull Player who, @NotNull Location location, @@ -48,42 +56,72 @@ public ScarecrowPlaceEvent( this.scarecrowItemID = scarecrowItemID; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { return getHandlerList(); } + /** + * Gets the item ID representing the scarecrow type being placed. + * + * @return the scarecrow item ID. + */ @NotNull public String getScarecrowItemID() { return scarecrowItemID; } /** - * Get the scarecrow location + * Gets the location where the scarecrow is being placed. * - * @return location + * @return the location of the scarecrow. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the state of the block where the scarecrow is placed. + * + * @return the block state of the scarecrow. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java index dee448baf..511f05b52 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java @@ -18,7 +18,7 @@ package net.momirealms.customcrops.api.event; import net.momirealms.customcrops.api.core.block.BreakReason; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.block.Block; @@ -30,7 +30,7 @@ import org.jetbrains.annotations.Nullable; /** - * An event that triggered when breaking a sprinkler + * An event that is triggered when a sprinkler is broken in the CustomCrops plugin. */ public class SprinklerBreakEvent extends Event implements Cancellable { @@ -43,6 +43,16 @@ public class SprinklerBreakEvent extends Event implements Cancellable { private final Block blockBreaker; private final BreakReason reason; + /** + * Constructor for the SprinklerBreakEvent. + * + * @param entityBreaker The entity that caused the sprinkler to break, if applicable (can be null). + * @param blockBreaker The block that caused the sprinkler to break, if applicable (can be null). + * @param location The location of the sprinkler being broken. + * @param blockState The state of the sprinkler block before it was broken. + * @param config The configuration of the sprinkler being broken. + * @param reason The reason why the sprinkler was broken. + */ public SprinklerBreakEvent( @Nullable Entity entityBreaker, @Nullable Block blockBreaker, @@ -59,21 +69,41 @@ public SprinklerBreakEvent( this.blockState = blockState; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -81,35 +111,60 @@ public HandlerList getHandlers() { } /** - * Get the sprinkler location + * Gets the location of the sprinkler being broken. * - * @return location + * @return the location of the sprinkler. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the entity responsible for breaking the sprinkler, if applicable. + * + * @return the entity that caused the break, or null if not applicable. + */ @Nullable public Entity getEntityBreaker() { return entityBreaker; } + /** + * Gets the state of the sprinkler block before it was broken. + * + * @return the block state of the sprinkler. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the configuration of the sprinkler being broken. + * + * @return the sprinkler configuration. + */ @NotNull public SprinklerConfig getSprinklerConfig() { return config; } + /** + * Gets the block responsible for breaking the sprinkler, if applicable. + * + * @return the block that caused the break, or null if not applicable. + */ @Nullable public Block getBlockBreaker() { return blockBreaker; } + /** + * Gets the reason for the sprinkler breakage. + * + * @return the reason for the break. + */ @NotNull public BreakReason getReason() { return reason; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java index d2d1d12fa..1da315fdc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.WateringMethod; import org.bukkit.Location; @@ -30,7 +30,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when a pot is watered by the fill-methods set in each sprinkler's config + * An event that is triggered when a sprinkler is filled with water using the fill methods defined in its configuration. */ public class SprinklerFillEvent extends PlayerEvent implements Cancellable { @@ -43,6 +43,17 @@ public class SprinklerFillEvent extends PlayerEvent implements Cancellable { private final CustomCropsBlockState blockState; private final EquipmentSlot hand; + /** + * Constructor for the SprinklerFillEvent. + * + * @param player The player who is filling the sprinkler. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param hand The hand (main or offhand) used by the player for filling. + * @param location The location of the sprinkler being filled. + * @param wateringMethod The method used to fill the sprinkler. + * @param blockState The state of the sprinkler block before it is filled. + * @param config The configuration of the sprinkler being filled. + */ public SprinklerFillEvent( @NotNull Player player, @NotNull ItemStack itemInHand, @@ -61,21 +72,41 @@ public SprinklerFillEvent( this.hand = hand; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -83,9 +114,9 @@ public HandlerList getHandlers() { } /** - * Get the pot location + * Gets the location of the sprinkler being filled. * - * @return location + * @return the location of the sprinkler. */ @NotNull public Location getLocation() { @@ -93,30 +124,50 @@ public Location getLocation() { } /** - * Get the item in hand + * Gets the ItemStack representing the item in the player's hand. * - * @return item in hand + * @return the item in hand. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the configuration of the sprinkler being filled. + * + * @return the sprinkler configuration. + */ @NotNull public SprinklerConfig getSprinklerConfig() { return config; } + /** + * Gets the method used to fill the sprinkler. + * + * @return the watering method. + */ @NotNull public WateringMethod getWateringMethod() { return wateringMethod; } + /** + * Gets the state of the sprinkler block before it is filled. + * + * @return the block state of the sprinkler. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the hand (main or offhand) used by the player to fill the sprinkler. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java index f3d5da340..ceb05b79f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -29,7 +29,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when interacting a sprinkler + * An event that is triggered when a player interacts with a sprinkler in the CustomCrops plugin. */ public class SprinklerInteractEvent extends PlayerEvent implements Cancellable { @@ -41,6 +41,16 @@ public class SprinklerInteractEvent extends PlayerEvent implements Cancellable { private final ItemStack itemInHand; private final EquipmentSlot hand; + /** + * Constructor for the SprinklerInteractEvent. + * + * @param who The player who is interacting with the sprinkler. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param location The location of the sprinkler being interacted with. + * @param config The configuration of the sprinkler being interacted with. + * @param blockState The state of the sprinkler block at the time of interaction. + * @param hand The hand (main or offhand) used by the player for the interaction. + */ public SprinklerInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @@ -57,21 +67,41 @@ public SprinklerInteractEvent( this.blockState = blockState; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -79,34 +109,49 @@ public HandlerList getHandlers() { } /** - * Get the sprinkler location + * Gets the location of the sprinkler being interacted with. * - * @return location + * @return the location of the sprinkler. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the state of the sprinkler block at the time of interaction. + * + * @return the block state of the sprinkler. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the configuration of the sprinkler being interacted with. + * + * @return the sprinkler configuration. + */ @NotNull public SprinklerConfig getSprinklerConfig() { return config; } + /** + * Gets the hand (main or offhand) used by the player to interact with the sprinkler. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } /** - * Get the item in player's hand + * Gets the ItemStack representing the item in the player's hand. * - * @return item in hand + * @return the item in hand. */ @NotNull public ItemStack getItemInHand() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java index f1cead1b3..ddbc370b1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -28,8 +28,9 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; + /** - * An event that triggered when placing a sprinkler + * An event that is triggered when a player places a sprinkler in the CustomCrops plugin. */ public class SprinklerPlaceEvent extends PlayerEvent implements Cancellable { @@ -41,6 +42,16 @@ public class SprinklerPlaceEvent extends PlayerEvent implements Cancellable { private final CustomCropsBlockState blockState; private final EquipmentSlot hand; + /** + * Constructor for the SprinklerPlaceEvent. + * + * @param who The player who is placing the sprinkler. + * @param itemInHand The ItemStack representing the item in the player's hand. + * @param hand The hand (main or offhand) used by the player to place the sprinkler. + * @param location The location where the sprinkler is being placed. + * @param config The configuration of the sprinkler being placed. + * @param blockState The state of the block where the sprinkler is placed. + */ public SprinklerPlaceEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @@ -57,21 +68,41 @@ public SprinklerPlaceEvent( this.blockState = blockState; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { this.cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @NotNull @Override public HandlerList getHandlers() { @@ -79,35 +110,50 @@ public HandlerList getHandlers() { } /** - * Get the item in player's hand + * Gets the ItemStack representing the item in the player's hand. * - * @return item in hand + * @return the item in hand. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the state of the block where the sprinkler is placed. + * + * @return the block state of the sprinkler. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } /** - * Get the sprinkler location + * Gets the location where the sprinkler is being placed. * - * @return location + * @return the location of the sprinkler. */ @NotNull public Location getLocation() { return location; } + /** + * Gets the configuration of the sprinkler being placed. + * + * @return the sprinkler configuration. + */ @NotNull public SprinklerConfig getSprinklerConfig() { return config; } + /** + * Gets the hand (main or offhand) used by the player to place the sprinkler. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java index ffadfaf9a..f2abc765b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanFillEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.misc.water.FillMethod; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -29,7 +29,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when player tries to add water to the watering-can + * An event that is triggered when a player attempts to add water to a watering can in the CustomCrops plugin. */ public class WateringCanFillEvent extends PlayerEvent implements Cancellable { @@ -41,6 +41,16 @@ public class WateringCanFillEvent extends PlayerEvent implements Cancellable { private final Location location; private final EquipmentSlot hand; + /** + * Constructor for the WateringCanFillEvent. + * + * @param player The player who is filling the watering can. + * @param hand The hand (main or offhand) used by the player to hold the watering can. + * @param itemInHand The ItemStack representing the watering can in the player's hand. + * @param location The location where the filling action is taking place. + * @param config The configuration of the watering can being filled. + * @param fillMethod The method used to fill the watering can. + */ public WateringCanFillEvent( @NotNull Player player, @NotNull EquipmentSlot hand, @@ -58,60 +68,90 @@ public WateringCanFillEvent( this.hand = hand; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { cancelled = cancel; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @Override public @NotNull HandlerList getHandlers() { return handlers; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } /** - * Get the watering can item + * Gets the ItemStack representing the watering can in the player's hand. * - * @return the watering can item + * @return the watering can item. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the configuration of the watering can being filled. + * + * @return the watering can configuration. + */ @NotNull public WateringCanConfig getConfig() { return config; } /** - * Get the positive fill method + * Gets the method used to fill the watering can. * - * @return positive fill method + * @return the fill method. */ @NotNull public FillMethod getFillMethod() { return fillMethod; } + /** + * Gets the hand (main or offhand) used by the player to hold the watering can. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } /** - * Get the location + * Gets the location where the filling action is taking place. * - * @return location + * @return the location of the action. */ @NotNull public Location getLocation() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java index d504dc2dd..91484b5cc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterPotEvent.java @@ -17,8 +17,8 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.PotConfig; -import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.core.world.Pos3; import net.momirealms.customcrops.common.util.Pair; import org.bukkit.entity.Player; @@ -32,7 +32,7 @@ import java.util.List; /** - * An event that triggered when player tries to use watering-can to add water to pots/sprinklers + * An event that is triggered when a player attempts to use a watering can to add water to pots or sprinklers. */ public class WateringCanWaterPotEvent extends PlayerEvent implements Cancellable { @@ -44,6 +44,16 @@ public class WateringCanWaterPotEvent extends PlayerEvent implements Cancellable private final PotConfig potConfig; private final List> potWithIDs; + /** + * Constructor for the WateringCanWaterPotEvent. + * + * @param player The player who is using the watering can. + * @param itemInHand The ItemStack representing the watering can in the player's hand. + * @param hand The hand (main or offhand) used by the player to hold the watering can. + * @param wateringCanConfig The configuration of the watering can being used. + * @param potConfig The configuration of the pot being watered. + * @param potWithIDs The list of pots with their positions and IDs. + */ public WateringCanWaterPotEvent( @NotNull Player player, @NotNull ItemStack itemInHand, @@ -61,51 +71,91 @@ public WateringCanWaterPotEvent( this.potWithIDs = potWithIDs; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { cancelled = cancel; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @Override public @NotNull HandlerList getHandlers() { return handlers; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } /** - * Get the watering can item + * Gets the ItemStack representing the watering can in the player's hand. * - * @return watering can item + * @return the watering can item. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the hand (main or offhand) used by the player to hold the watering can. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } + /** + * Gets the configuration of the watering can being used. + * + * @return the watering can configuration. + */ @NotNull public WateringCanConfig getWateringCanConfig() { return wateringCanConfig; } + /** + * Gets the configuration of the pot being watered. + * + * @return the pot configuration. + */ @NotNull public PotConfig getPotConfig() { return potConfig; } + /** + * Gets the list of pots with their positions and IDs. + * + * @return the list of pots with positions and IDs. + */ @NotNull public List> getPotWithIDs() { return potWithIDs; diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java index 9cf0aa212..860980d36 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/WateringCanWaterSprinklerEvent.java @@ -17,8 +17,8 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; -import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -30,7 +30,7 @@ import org.jetbrains.annotations.NotNull; /** - * An event that triggered when player tries to use watering-can to add water to pots/sprinklers + * An event that is triggered when a player attempts to use a watering can to add water to a sprinkler in the CustomCrops plugin. */ public class WateringCanWaterSprinklerEvent extends PlayerEvent implements Cancellable { @@ -43,6 +43,17 @@ public class WateringCanWaterSprinklerEvent extends PlayerEvent implements Cance private final CustomCropsBlockState blockState; private final Location location; + /** + * Constructor for the WateringCanWaterSprinklerEvent. + * + * @param player The player who is using the watering can. + * @param itemInHand The ItemStack representing the watering can in the player's hand. + * @param hand The hand (main or offhand) used by the player to hold the watering can. + * @param wateringCanConfig The configuration of the watering can being used. + * @param sprinklerConfig The configuration of the sprinkler being watered. + * @param blockState The state of the block where the sprinkler is located. + * @param location The location of the sprinkler being watered. + */ public WateringCanWaterSprinklerEvent( @NotNull Player player, @NotNull ItemStack itemInHand, @@ -62,56 +73,101 @@ public WateringCanWaterSprinklerEvent( this.location = location; } + /** + * Returns whether the event is cancelled. + * + * @return true if the event is cancelled, false otherwise. + */ @Override public boolean isCancelled() { return cancelled; } + /** + * Sets the cancelled state of the event. + * + * @param cancel true to cancel the event, false otherwise. + */ @Override public void setCancelled(boolean cancel) { cancelled = cancel; } + /** + * Gets the list of handlers for this event instance. + * + * @return the handler list. + */ @Override public @NotNull HandlerList getHandlers() { return handlers; } + /** + * Gets the list of handlers for this event. + * + * @return the static handler list. + */ @NotNull public static HandlerList getHandlerList() { return handlers; } /** - * Get the watering can item + * Gets the ItemStack representing the watering can in the player's hand. * - * @return watering can item + * @return the watering can item. */ @NotNull public ItemStack getItemInHand() { return itemInHand; } + /** + * Gets the hand (main or offhand) used by the player to hold the watering can. + * + * @return the equipment slot representing the hand used. + */ @NotNull public EquipmentSlot getHand() { return hand; } + /** + * Gets the configuration of the watering can being used. + * + * @return the watering can configuration. + */ @NotNull public WateringCanConfig getWateringCanConfig() { return wateringCanConfig; } + /** + * Gets the configuration of the sprinkler being watered. + * + * @return the sprinkler configuration. + */ @NotNull public SprinklerConfig getSprinklerConfig() { return sprinklerConfig; } + /** + * Gets the state of the block where the sprinkler is located. + * + * @return the block state of the sprinkler. + */ @NotNull public CustomCropsBlockState getBlockState() { return blockState; } + /** + * Gets the location of the sprinkler being watered. + * + * @return the location of the sprinkler. + */ @NotNull public Location getLocation() { return location; 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 aa3fb72c1..d52a8d301 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 @@ -23,12 +23,12 @@ import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.ConfigManager; -import net.momirealms.customcrops.api.core.block.CrowAttack; +import net.momirealms.customcrops.api.core.mechanic.crop.CrowAttack; import net.momirealms.customcrops.api.core.block.GreenhouseBlock; import net.momirealms.customcrops.api.core.block.PotBlock; import net.momirealms.customcrops.api.core.block.ScarecrowBlock; -import net.momirealms.customcrops.api.core.item.Fertilizer; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; +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; 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 b92c28ccd..df85741e6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -22,6 +22,7 @@ import net.momirealms.customcrops.api.core.SimpleRegistryAccess; import net.momirealms.customcrops.api.core.block.*; import net.momirealms.customcrops.api.core.item.*; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; import net.momirealms.customcrops.api.misc.HologramManager; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java index c4d0e3072..13d7e9adf 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/BlockActionManager.java @@ -27,9 +27,9 @@ import net.momirealms.customcrops.api.core.FurnitureRotation; import net.momirealms.customcrops.api.core.block.CropBlock; import net.momirealms.customcrops.api.core.block.PotBlock; -import net.momirealms.customcrops.api.core.block.VariationData; -import net.momirealms.customcrops.api.core.item.Fertilizer; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.VariationData; +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.CustomCropsChunk; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java index 84a7d8dc7..dfb286513 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java @@ -28,7 +28,7 @@ import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; import net.momirealms.customcrops.api.core.block.SprinklerBlock; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java index 6c3a726dd..e77a2488c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -30,9 +30,13 @@ import dev.dejvokep.boostedyaml.utils.format.NodeRole; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.block.*; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; -import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.DeathCondition; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.common.helper.AdventureHelper; import net.momirealms.customcrops.common.locale.TranslationManager; import net.momirealms.customcrops.common.plugin.CustomCropsProperties; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java index df56e6dee..cd8748d36 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -25,13 +25,13 @@ import net.momirealms.customcrops.api.core.CustomForm; import net.momirealms.customcrops.api.core.ExistenceForm; import net.momirealms.customcrops.api.core.Registries; -import net.momirealms.customcrops.api.core.block.CropConfig; -import net.momirealms.customcrops.api.core.block.CropStageConfig; -import net.momirealms.customcrops.api.core.block.PotConfig; -import net.momirealms.customcrops.api.core.block.SprinklerConfig; -import net.momirealms.customcrops.api.core.item.FertilizerConfig; -import net.momirealms.customcrops.api.core.item.FertilizerType; -import net.momirealms.customcrops.api.core.item.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.misc.water.WaterBar; From 32f01f1aecc2ce331a105ce9dbeae54f5efa7a17 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 01:42:37 +0800 Subject: [PATCH 077/329] add comments --- .../api/BukkitCustomCropsPlugin.java | 94 +++++- .../api/core/BuiltInBlockMechanics.java | 39 ++- .../api/core/BuiltInItemMechanics.java | 28 +- .../api/core/ClearableMappedRegistry.java | 25 +- .../api/core/ClearableRegistry.java | 14 +- .../customcrops/api/core/ConfigManager.java | 30 ++ .../api/core/CustomItemProvider.java | 62 ++++ .../api/core/FurnitureRotation.java | 6 +- .../customcrops/api/core/IdMap.java | 45 ++- .../customcrops/api/core/ItemManager.java | 145 ++++++++- .../customcrops/api/core/MappedRegistry.java | 64 ++++ .../customcrops/api/core/Registry.java | 50 +++- .../customcrops/api/core/RegistryAccess.java | 34 +++ .../customcrops/api/core/StatedItem.java | 26 -- .../api/core/SynchronizedCompoundMap.java | 47 ++- .../api/core/WriteableRegistry.java | 17 +- .../customcrops/api/core/block/PotBlock.java | 3 + .../api/core/block/SprinklerBlock.java | 6 +- .../api/core/mechanic/crop/BoneMeal.java | 51 ++++ .../api/core/mechanic/crop/CropConfig.java | 238 +++++++++++++++ .../core/mechanic/crop/CropStageConfig.java | 128 ++++++++ .../core/mechanic/crop/DeathCondition.java | 37 ++- .../api/core/mechanic/crop/GrowCondition.java | 26 +- .../api/core/mechanic/crop/VariationData.java | 25 ++ .../core/mechanic/fertilizer/Fertilizer.java | 60 +++- .../mechanic/fertilizer/FertilizerConfig.java | 90 +++++- .../api/core/mechanic/pot/PotConfig.java | 244 ++++++++++++++++ .../mechanic/sprinkler/SprinklerConfig.java | 276 +++++++++++++++++- .../sprinkler/SprinklerConfigImpl.java | 17 +- .../wateringcan/WateringCanConfig.java | 249 +++++++++++++++- .../customcrops/api/core/world/BlockPos.java | 74 ++++- .../customcrops/api/core/world/ChunkPos.java | 57 +++- .../api/core/world/CustomCropsBlockState.java | 15 + .../api/core/world/CustomCropsChunk.java | 138 ++++++--- .../api/core/world/CustomCropsRegion.java | 53 ++++ .../api/core/world/CustomCropsRegionImpl.java | 1 + .../api/core/world/CustomCropsSection.java | 55 ++++ .../api/core/world/CustomCropsWorld.java | 181 ++++++++---- .../api/core/world/CustomCropsWorldImpl.java | 3 +- .../customcrops/api/core/world/DataBlock.java | 26 +- .../customcrops/api/core/world/Pos3.java | 56 +++- .../customcrops/api/core/world/RegionPos.java | 49 ++++ .../api/core/world/WorldExtraData.java | 57 +++- .../api/core/world/WorldManager.java | 74 ++++- .../api/core/world/WorldSetting.java | 137 +++++++++ .../api/core/world/adaptor/WorldAdaptor.java | 70 ++++- .../api/misc/value/DynamicText.java | 76 ----- .../customcrops/api/misc/value/MathValue.java | 2 +- .../customcrops/api/misc/value/TextValue.java | 2 +- .../adaptor/asp_r1/SlimeWorldAdaptorR1.java | 5 - .../bukkit/BukkitCustomCropsPluginImpl.java | 7 + .../customcrops/bukkit/config/ConfigType.java | 3 +- .../adaptor/BukkitWorldAdaptor.java | 5 - .../bukkit/item/BukkitItemManager.java | 6 +- .../bukkit/world/BukkitWorldManager.java | 4 +- 55 files changed, 3035 insertions(+), 297 deletions(-) delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java delete mode 100644 api/src/main/java/net/momirealms/customcrops/api/misc/value/DynamicText.java 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 cdf77e6c4..20fea652c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java +++ b/api/src/main/java/net/momirealms/customcrops/api/BukkitCustomCropsPlugin.java @@ -36,10 +36,12 @@ import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; public abstract class BukkitCustomCropsPlugin implements CustomCropsPlugin { @@ -75,7 +77,7 @@ public BukkitCustomCropsPlugin(Plugin boostrap) { } /** - * Retrieves the singleton instance of BukkitCustomFishingPlugin. + * Retrieves the singleton instance of BukkitCustomCropsPlugin. * * @return the singleton instance * @throws IllegalArgumentException if the plugin is not initialized @@ -116,46 +118,94 @@ public TranslationManager getTranslationManager() { return translationManager; } + /** + * Retrieves the ItemManager. + * + * @return the {@link AbstractItemManager} + */ + @NotNull public AbstractItemManager getItemManager() { return itemManager; } + /** + * Retrieves the SchedulerAdapter. + * + * @return the {@link SchedulerAdapter} + */ @Override + @NotNull public SchedulerAdapter getScheduler() { return scheduler; } + /** + * Retrieves the SenderFactory. + * + * @return the {@link SenderFactory} + */ + @NotNull public SenderFactory getSenderFactory() { return senderFactory; } + /** + * Retrieves the WorldManager. + * + * @return the {@link WorldManager} + */ + @NotNull public WorldManager getWorldManager() { return worldManager; } + /** + * Retrieves the IntegrationManager. + * + * @return the {@link IntegrationManager} + */ + @NotNull public IntegrationManager getIntegrationManager() { return integrationManager; } + /** + * Retrieves the PlaceholderManager. + * + * @return the {@link PlaceholderManager} + */ + @NotNull public PlaceholderManager getPlaceholderManager() { return placeholderManager; } /** - * Logs a debug message. + * Retrieves the CoolDownManager. * - * @param message the message to log + * @return the {@link CoolDownManager} */ - public abstract void debug(Object message); - - public File getDataFolder() { - return boostrap.getDataFolder(); + @NotNull + public CoolDownManager getCoolDownManager() { + return coolDownManager; } + /** + * Retrieves the RegistryAccess. + * + * @return the {@link RegistryAccess} + */ + @NotNull public RegistryAccess getRegistryAccess() { return registryAccess; } + /** + * Retrieves an ActionManager for a specific type. + * + * @param type the class type of the action + * @return the {@link ActionManager} for the specified type + * @throws IllegalArgumentException if the type is null + */ @SuppressWarnings("unchecked") public ActionManager getActionManager(Class type) { if (type == null) { @@ -164,6 +214,13 @@ public ActionManager getActionManager(Class type) { return (ActionManager) actionManagers.get(type); } + /** + * Retrieves a RequirementManager for a specific type. + * + * @param type the class type of the requirement + * @return the {@link RequirementManager} for the specified type + * @throws IllegalArgumentException if the type is null + */ @SuppressWarnings("unchecked") public RequirementManager getRequirementManager(Class type) { if (type == null) { @@ -172,7 +229,26 @@ public RequirementManager getRequirementManager(Class type) { return (RequirementManager) instance.requirementManagers.get(type); } - public CoolDownManager getCoolDownManager() { - return coolDownManager; + /** + * Logs a debug message. + * + * @param message the message to log + */ + public abstract void debug(Object message); + + /** + * Logs a debug message using a {@link Supplier}. + * + * @param message the message supplier to log + */ + public abstract void debug(Supplier message); + + /** + * Retrieves the data folder for the plugin. + * + * @return the data folder as a {@link File} + */ + public File getDataFolder() { + return boostrap.getDataFolder(); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java index 3bbc3ebeb..98e56d73d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInBlockMechanics.java @@ -24,6 +24,9 @@ import java.util.Objects; +/** + * BuiltInBlockMechanics defines a set of standard block mechanics for the Custom Crops plugin. + */ public class BuiltInBlockMechanics { public static final BuiltInBlockMechanics CROP = create("crop"); @@ -35,26 +38,58 @@ public class BuiltInBlockMechanics { private final Key key; - public BuiltInBlockMechanics(Key key) { + /** + * Constructs a new BuiltInBlockMechanics with a unique key. + * + * @param key the unique key for this mechanic + */ + private BuiltInBlockMechanics(Key key) { this.key = key; } + /** + * Factory method to create a new BuiltInBlockMechanics instance with the specified ID. + * + * @param id the ID of the mechanic + * @return a new BuiltInBlockMechanics instance + */ static BuiltInBlockMechanics create(String id) { - return new BuiltInBlockMechanics(Key.key("customcrops", id)); + return new BuiltInBlockMechanics(Key.key("customcrops", id)); } + /** + * Retrieves the unique key associated with this block mechanic. + * + * @return the key + */ public Key key() { return key; } + /** + * Creates a new CustomCropsBlockState using the associated block mechanic. + * + * @return a new CustomCropsBlockState + */ public CustomCropsBlockState createBlockState() { return mechanic().createBlockState(); } + /** + * Creates a new CustomCropsBlockState using the associated block mechanic and provided data. + * + * @param data the compound map data for the block state + * @return a new CustomCropsBlockState + */ public CustomCropsBlockState createBlockState(CompoundMap data) { return mechanic().createBlockState(data); } + /** + * Retrieves the CustomCropsBlock associated with this block mechanic. + * + * @return the CustomCropsBlock + */ public CustomCropsBlock mechanic() { return Objects.requireNonNull(Registries.BLOCK.get(key)); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java index 4043dc01c..97bef0832 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/BuiltInItemMechanics.java @@ -20,6 +20,9 @@ import net.momirealms.customcrops.api.core.item.CustomCropsItem; import net.momirealms.customcrops.common.util.Key; +/** + * BuiltInItemMechanics defines a set of standard item mechanics for the Custom Crops plugin. + */ public class BuiltInItemMechanics { public static final BuiltInItemMechanics WATERING_CAN = create("watering_can"); @@ -29,18 +32,39 @@ public class BuiltInItemMechanics { private final Key key; - public BuiltInItemMechanics(Key key) { + /** + * Constructs a new BuiltInItemMechanics with a unique key. + * + * @param key the unique key for this mechanic + */ + private BuiltInItemMechanics(Key key) { this.key = key; } + /** + * Factory method to create a new BuiltInItemMechanics instance with the specified ID. + * + * @param id the ID of the mechanic + * @return a new BuiltInItemMechanics instance + */ static BuiltInItemMechanics create(String id) { - return new BuiltInItemMechanics(Key.key("customcrops", id)); + return new BuiltInItemMechanics(Key.key("customcrops", id)); } + /** + * Retrieves the unique key associated with this item mechanic. + * + * @return the key + */ public Key key() { return key; } + /** + * Retrieves the CustomCropsItem associated with this item mechanic. + * + * @return the CustomCropsItem + */ public CustomCropsItem mechanic() { return Registries.ITEM.get(key); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java index 81ea6f6a8..b5fb59f87 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableMappedRegistry.java @@ -19,16 +19,33 @@ import net.momirealms.customcrops.common.util.Key; +/** + * ClearableMappedRegistry is a concrete implementation of the ClearableRegistry interface. + * It extends MappedRegistry and provides the capability to clear all entries. + * + * @param the type of keys maintained by this registry + * @param the type of mapped values + */ public class ClearableMappedRegistry extends MappedRegistry implements ClearableRegistry { + /** + * Constructs a new ClearableMappedRegistry with a unique key. + * + * @param key the unique key for this registry + */ public ClearableMappedRegistry(Key key) { super(key); } + /** + * Clears all entries from the registry. + * This operation removes all key-value mappings from the registry, + * leaving it empty. + */ @Override public void clear() { - super.byID.clear(); - super.byKey.clear(); - super.byValue.clear(); + super.byID.clear(); // Clears the list of values indexed by ID + super.byKey.clear(); // Clears the map of keys to values + super.byValue.clear(); // Clears the map of values to keys } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java index 219b822e7..92def0832 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ClearableRegistry.java @@ -17,7 +17,19 @@ package net.momirealms.customcrops.api.core; +/** + * The ClearableRegistry interface extends WriteableRegistry and provides + * an additional method to clear all entries from the registry. + * + * @param the type of keys maintained by this registry + * @param the type of mapped values + */ public interface ClearableRegistry extends WriteableRegistry { + /** + * Clears all entries from the registry. + * This operation removes all key-value mappings from the registry, + * leaving it empty. + */ void clear(); -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java index e5189c90f..0b2e1ee36 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -247,14 +247,44 @@ protected Path resolveConfig(String filePath) { return configFile; } + /** + * Registers a new WateringCan configuration to the plugin. + * Call this method in {@link net.momirealms.customcrops.api.event.CustomCropsReloadEvent} otherwise the config would lose on each reload + * + * @param config the WateringCanConfig object to register + */ public abstract void registerWateringCanConfig(WateringCanConfig config); + /** + * Registers a new Fertilizer configuration to the plugin. + * Call this method in {@link net.momirealms.customcrops.api.event.CustomCropsReloadEvent} otherwise the config would lose on each reload + * + * @param config the FertilizerConfig object to register + */ public abstract void registerFertilizerConfig(FertilizerConfig config); + /** + * Registers a new Crop configuration to the plugin. + * Call this method in {@link net.momirealms.customcrops.api.event.CustomCropsReloadEvent} otherwise the config would lose on each reload + * + * @param config the CropConfig object to register + */ public abstract void registerCropConfig(CropConfig config); + /** + * Registers a new Pot configuration to the plugin. + * Call this method in {@link net.momirealms.customcrops.api.event.CustomCropsReloadEvent} otherwise the config would lose on each reload + * + * @param config the PotConfig object to register + */ public abstract void registerPotConfig(PotConfig config); + /** + * Registers a new Sprinkler configuration to the plugin. + * Call this method in {@link net.momirealms.customcrops.api.event.CustomCropsReloadEvent} otherwise the config would lose on each reload + * + * @param config the SprinklerConfig object to register + */ public abstract void registerSprinklerConfig(SprinklerConfig config); public WateringMethod[] getWateringMethods(Section section) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java b/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java index 2d62a3f6b..3abc1f7ba 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/CustomItemProvider.java @@ -24,28 +24,90 @@ import org.bukkit.inventory.ItemStack; import org.checkerframework.checker.nullness.qual.Nullable; +/** + * Interface defining methods for managing custom items, blocks, and furniture in the CustomCrops plugin. + * This interface provides methods for placing and removing custom blocks and furniture, as well as + * retrieving IDs and ItemStacks associated with custom items. + */ public interface CustomItemProvider { + /** + * Removes a custom block at the specified location. + * + * @param location The location from which to remove the custom block. + * @return true if the block was successfully removed, false otherwise. + */ boolean removeCustomBlock(Location location); + /** + * Places a custom block at the specified location. + * + * @param location The location where the custom block should be placed. + * @param id The ID of the custom block to place. + * @return true if the block was successfully placed, false otherwise. + */ boolean placeCustomBlock(Location location, String id); + /** + * Places a piece of custom furniture at the specified location. + * + * @param location The location where the furniture should be placed. + * @param id The ID of the furniture to place. + * @return The entity representing the placed furniture, or null if placement failed. + */ @Nullable Entity placeFurniture(Location location, String id); + /** + * Removes a piece of custom furniture represented by the specified entity. + * + * @param entity The entity representing the furniture to remove. + * @return true if the furniture was successfully removed, false otherwise. + */ boolean removeFurniture(Entity entity); + /** + * Retrieves the ID of a custom block at the specified block. + * + * @param block The block to check. + * @return The ID of the custom block, or null if no custom block is found. + */ @Nullable String blockID(Block block); + /** + * Retrieves the ID of a custom item represented by the provided ItemStack. + * + * @param itemStack The ItemStack to check. + * @return The ID of the custom item, or null if no custom item is found. + */ @Nullable String itemID(ItemStack itemStack); + /** + * Creates an ItemStack for a custom item based on the given item ID and player. + * + * @param player The player for whom the item is being created. + * @param id The ID of the custom item. + * @return The constructed ItemStack, or null if creation fails. + */ @Nullable ItemStack itemStack(Player player, String id); + /** + * Retrieves the ID of a custom furniture item associated with the specified entity. + * + * @param entity The entity to check. + * @return The ID of the furniture, or null if no furniture is found. + */ @Nullable String furnitureID(Entity entity); + /** + * Determines if the specified entity is a piece of custom furniture. + * + * @param entity The entity to check. + * @return true if the entity is a piece of custom furniture, false otherwise. + */ boolean isFurniture(Entity entity); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java b/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java index 57409f992..775421ba0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/FurnitureRotation.java @@ -44,9 +44,6 @@ public static FurnitureRotation random() { public static FurnitureRotation getByRotation(Rotation rotation) { switch (rotation) { - default -> { - return FurnitureRotation.SOUTH; - } case CLOCKWISE -> { return FurnitureRotation.WEST; } @@ -56,6 +53,9 @@ public static FurnitureRotation getByRotation(Rotation rotation) { case FLIPPED -> { return FurnitureRotation.NORTH; } + default -> { + return FurnitureRotation.SOUTH; + } } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java b/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java index c9e7b5471..f29603b99 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/IdMap.java @@ -17,16 +17,45 @@ package net.momirealms.customcrops.api.core; -import javax.annotation.Nullable; +import org.jetbrains.annotations.Nullable; +/** + * The IdMap interface defines a structure for managing and retrieving objects + * by their unique identifiers (IDs). + * + * @param the type of objects managed by this IdMap + */ public interface IdMap extends Iterable { + + /** + * The default value used to indicate that no ID is assigned or available. + */ int DEFAULT = -1; + /** + * Retrieves the unique identifier associated with a given object. + * + * @param value the object whose ID is to be retrieved + * @return the unique identifier of the object, or -1 if not found + */ int getId(T value); + /** + * Retrieves an object by its unique identifier. + * + * @param index the unique identifier of the object + * @return the object associated with the given ID, or null if not present + */ @Nullable T byId(int index); + /** + * Retrieves an object by its unique identifier or throws an exception if not found. + * + * @param index the unique identifier of the object + * @return the object associated with the given ID + * @throws IllegalArgumentException if no object with the given ID exists + */ default T byIdOrThrow(int index) { T object = this.byId(index); if (object == null) { @@ -36,6 +65,13 @@ default T byIdOrThrow(int index) { } } + /** + * Retrieves the unique identifier associated with a given object or throws an exception if not found. + * + * @param value the object whose ID is to be retrieved + * @return the unique identifier of the object + * @throws IllegalArgumentException if no ID for the given object exists + */ default int getIdOrThrow(T value) { int i = this.getId(value); if (i == -1) { @@ -45,5 +81,10 @@ default int getIdOrThrow(T value) { } } + /** + * Returns the total number of entries in this IdMap. + * + * @return the number of entries + */ int size(); -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java index 41bfacccb..2adb9ef8a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ItemManager.java @@ -27,55 +27,186 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * Interface defining methods for managing custom items, blocks, and furniture in the CustomCrops plugin. + * It includes methods for placing and removing items and blocks, interacting with custom events, and managing item data. + */ public interface ItemManager extends Reloadable { + /** + * Places an item at the specified location. + * + * @param location The location where the item should be placed. + * @param form The existence form of the item (e.g., block or furniture). + * @param id The ID of the item to place. + * @param rotation The rotation of the furniture, if applicable. + */ void place(@NotNull Location location, @NotNull ExistenceForm form, @NotNull String id, FurnitureRotation rotation); + /** + * Removes an item from the specified location. + * + * @param location The location from which the item should be removed. + * @param form The existence form of the item (e.g., block or furniture). + * @return The rotation of the removed furniture, or NONE if no furniture was found. + */ + @NotNull FurnitureRotation remove(@NotNull Location location, @NotNull ExistenceForm form); + /** + * Places a custom block at the specified location. + * + * @param location The location where the block should be placed. + * @param id The ID of the block to place. + */ void placeBlock(@NotNull Location location, @NotNull String id); + /** + * Places a piece of furniture at the specified location. + * + * @param location The location where the furniture should be placed. + * @param id The ID of the furniture to place. + * @param rotation The rotation of the furniture. + */ void placeFurniture(@NotNull Location location, @NotNull String id, FurnitureRotation rotation); + /** + * Removes a custom block from the specified location. + * + * @param location The location from which the block should be removed. + */ void removeBlock(@NotNull Location location); - @Nullable + /** + * Removes furniture from the specified location. + * + * @param location The location from which the furniture should be removed. + * @return The rotation of the removed furniture, or NONE if no furniture was found. + */ + @NotNull FurnitureRotation removeFurniture(@NotNull Location location); + /** + * Retrieves the ID of the custom block at the specified location. + * + * @param location The location of the block. + * @return The ID of the block. + */ @NotNull default String blockID(@NotNull Location location) { return blockID(location.getBlock()); } + /** + * Retrieves the ID of the custom block at the specified block. + * + * @param block The block to check. + * @return The ID of the block. + */ @NotNull String blockID(@NotNull Block block); + /** + * Retrieves the ID of the furniture attached to the specified entity. + * + * @param entity The entity to check. + * @return The ID of the furniture, or null if not applicable. + */ @Nullable String furnitureID(@NotNull Entity entity); + /** + * Retrieves the ID of the custom entity. + * + * @param entity The entity to check. + * @return The ID of the entity. + */ @NotNull String entityID(@NotNull Entity entity); + /** + * Retrieves the ID of the furniture at the specified location. + * + * @param location The location to check. + * @return The ID of the furniture, or null if no furniture is found. + */ @Nullable String furnitureID(Location location); + /** + * Retrieves the ID of any custom item at the specified location. + * + * @param location The location to check. + * @return The ID of any custom item. + */ @NotNull String anyID(Location location); + /** + * Retrieves the ID of the item at the specified location based on the existence form. + * + * @param location The location to check. + * @param form The existence form of the item. + * @return The ID of the item, or null if not found. + */ @Nullable String id(Location location, ExistenceForm form); + /** + * Sets a custom event listener for handling custom item events. + * + * @param listener The custom event listener to set. + */ void setCustomEventListener(@NotNull AbstractCustomEventListener listener); + /** + * Sets a custom item provider for managing custom items. + * + * @param provider The custom item provider to set. + */ void setCustomItemProvider(@NotNull CustomItemProvider provider); - String id(ItemStack itemStack); - + /** + * Retrieves the ID of the custom item represented by the provided ItemStack. + * + * @param itemStack The ItemStack to check. + * @return The ID of the custom item. + */ + @NotNull + String id(@Nullable ItemStack itemStack); + + /** + * Builds an ItemStack for the player based on the given item ID. + * + * @param player The player for whom the item is being built. + * @param id The ID of the item to build. + * @return The constructed ItemStack, or null if construction fails. + */ @Nullable - ItemStack build(Player player, String id); - - Item wrap(ItemStack itemStack); - + ItemStack build(@Nullable Player player, @NotNull String id); + + /** + * Wraps an ItemStack into a custom item wrapper. + * + * @param itemStack The ItemStack to wrap. + * @return The wrapped custom item. + */ + Item wrap(@NotNull ItemStack itemStack); + + /** + * Decreases the damage on the provided item. + * + * @param player The item. + * @param itemStack The item to modify. + * @param amount The amount of damage to decrease. + */ void decreaseDamage(Player player, ItemStack itemStack, int amount); + /** + * Increases the damage on the provided item. + * + * @param holder The item. + * @param itemStack The item to modify. + * @param amount The amount of damage to increase. + */ void increaseDamage(Player holder, ItemStack itemStack, int amount); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java index 153986d32..054e528b0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/MappedRegistry.java @@ -23,6 +23,13 @@ import java.util.*; +/** + * A registry implementation that supports mapping keys to values and provides + * methods for registration, lookup, and iteration. + * + * @param the type of the keys used for lookup + * @param the type of the values stored in the registry + */ public class MappedRegistry implements WriteableRegistry { protected final Map byKey = new HashMap<>(1024); @@ -30,54 +37,111 @@ public class MappedRegistry implements WriteableRegistry { protected final ArrayList byID = new ArrayList<>(1024); private final Key key; + /** + * Constructs a new MappedRegistry with a given unique key. + * + * @param key the unique key for this registry + */ public MappedRegistry(Key key) { this.key = key; } + /** + * Registers a new key-value pair in the registry. + * + * @param key the key associated with the value + * @param value the value to be registered + */ @Override public void register(K key, T value) { + if (byKey.containsKey(key)) return; byKey.put(key, value); byValue.put(value, key); byID.add(value); } + /** + * Gets the unique key identifier for this registry. + * + * @return the key of the registry + */ @Override public Key key() { return key; } + /** + * Retrieves the index (ID) of a given value in the registry. + * + * @param value the value to look up + * @return the index of the value, or -1 if not found + */ @Override public int getId(@Nullable T value) { return byID.indexOf(value); } + /** + * Retrieves a value from the registry by its index (ID). + * + * @param index the index of the value + * @return the value at the specified index, or null if out of bounds + */ @Nullable @Override public T byId(int index) { return byID.get(index); } + /** + * Gets the number of entries in the registry. + * + * @return the size of the registry + */ @Override public int size() { return byKey.size(); } + /** + * Retrieves a value from the registry by its key. + * + * @param key the key of the value + * @return the value associated with the key, or null if not found + */ @Nullable @Override public T get(@Nullable K key) { return byKey.get(key); } + /** + * Checks if the registry contains a given key. + * + * @param key the key to check + * @return true if the key exists, false otherwise + */ @Override public boolean containsKey(@Nullable K key) { return byKey.containsKey(key); } + /** + * Checks if the registry contains a given value. + * + * @param value the value to check + * @return true if the value exists, false otherwise + */ @Override public boolean containsValue(@Nullable T value) { return byValue.containsKey(value); } + /** + * Provides an iterator over the values in the registry. + * + * @return an iterator for the registry values + */ @NotNull @Override public Iterator iterator() { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java index 5f7cfa467..e6f8fc53e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registry.java @@ -18,20 +18,56 @@ package net.momirealms.customcrops.api.core; import net.momirealms.customcrops.common.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; - +/** + * The Registry interface defines a structure for a key-value mapping system + * that supports efficient retrieval and management of entries. + * + * @param the type of keys maintained by this registry + * @param the type of values that the keys map to + */ public interface Registry extends IdMap { + /** + * Retrieves the unique key associated with this registry. + * + * @return the unique {@link Key} of this registry + */ Key key(); + /** + * Retrieves the unique identifier associated with a value. + * + * @param value the value whose identifier is to be retrieved + * @return the unique identifier of the value, or -1 if not present + */ @Override - int getId(@Nullable T value); + int getId(@NotNull T value); + /** + * Retrieves a value mapped to the specified key. + * + * @param key the key associated with the value to be retrieved + * @return the value mapped to the specified key, or null if no mapping exists + */ @Nullable - T get(@Nullable K key); + T get(@NotNull K key); - boolean containsKey(@Nullable K key); + /** + * Checks if the registry contains a mapping for the specified key. + * + * @param key the key to check for existence + * @return true if the registry contains a mapping for the key, false otherwise + */ + boolean containsKey(@NotNull K key); - boolean containsValue(@Nullable T value); -} + /** + * Checks if the registry contains the specified value. + * + * @param value the value to check for existence + * @return true if the registry contains the specified value, false otherwise + */ + boolean containsValue(@NotNull T value); +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java index ef679f38c..2a1d0229f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/RegistryAccess.java @@ -22,17 +22,51 @@ import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; import net.momirealms.customcrops.common.util.Key; +/** + * Interface defining methods for registering and accessing different types of custom crop mechanics + * such as blocks, items, and fertilizer types in the CustomCrops plugin. + */ public interface RegistryAccess { + /** + * Registers a new custom crop block mechanic. + * + * @param block The custom crop block to register. + */ void registerBlockMechanic(CustomCropsBlock block); + /** + * Registers a new custom crop item mechanic. + * + * @param item The custom crop item to register. + */ void registerItemMechanic(CustomCropsItem item); + /** + * Registers a new fertilizer type mechanic. + * + * @param type The fertilizer type to register. + */ void registerFertilizerType(FertilizerType type); + /** + * Retrieves the registry containing all registered custom crop blocks. + * + * @return the block registry + */ Registry getBlockRegistry(); + /** + * Retrieves the registry containing all registered custom crop items. + * + * @return the item registry + */ Registry getItemRegistry(); + /** + * Retrieves the registry containing all registered fertilizer types. + * + * @return the fertilizer type registry + */ Registry getFertilizerTypeRegistry(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java deleted file mode 100644 index 4dc3573fb..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/core/StatedItem.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.core; - -import net.momirealms.customcrops.api.context.Context; - -@FunctionalInterface -public interface StatedItem { - - String currentState(Context context); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java index 48af31c19..e51f34f1e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SynchronizedCompoundMap.java @@ -28,6 +28,10 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +/** + * A thread-safe wrapper around a CompoundMap that provides synchronized access + * to the underlying map for reading and writing operations. + */ public class SynchronizedCompoundMap { private final CompoundMap compoundMap; @@ -35,14 +39,30 @@ public class SynchronizedCompoundMap { private final java.util.concurrent.locks.Lock readLock = rwLock.readLock(); private final java.util.concurrent.locks.Lock writeLock = rwLock.writeLock(); + /** + * Constructs a new SynchronizedCompoundMap with the specified CompoundMap. + * + * @param compoundMap the underlying CompoundMap to wrap + */ public SynchronizedCompoundMap(CompoundMap compoundMap) { this.compoundMap = compoundMap; } + /** + * Returns the original underlying CompoundMap. + * + * @return the original CompoundMap + */ public CompoundMap originalMap() { return compoundMap; } + /** + * Retrieves a Tag from the map using the specified key. + * + * @param key the key to look up + * @return the Tag associated with the key, or null if not found + */ public Tag get(String key) { readLock.lock(); try { @@ -52,6 +72,14 @@ public Tag get(String key) { } } + /** + * Puts a Tag into the map with the specified key, returning the previous + * value associated with the key, if any. + * + * @param key the key to associate with the Tag + * @param tag the Tag to insert + * @return the previous Tag associated with the key, or null if none + */ public Tag put(String key, Tag tag) { writeLock.lock(); try { @@ -61,6 +89,12 @@ public Tag put(String key, Tag tag) { } } + /** + * Removes a Tag from the map with the specified key. + * + * @param key the key whose mapping is to be removed + * @return the Tag previously associated with the key, or null if none + */ public Tag remove(String key) { writeLock.lock(); try { @@ -83,13 +117,22 @@ public String toString() { return compoundMapToString("BlockData", compoundMap); } + /** + * Recursively converts a CompoundMap to a string representation. + * + * @param key the key associated with the CompoundMap + * @param compoundMap the CompoundMap to convert + * @return a string representation of the CompoundMap + */ + @SuppressWarnings("unchecked") private String compoundMapToString(String key, CompoundMap compoundMap) { StringJoiner joiner = new StringJoiner(", "); for (Map.Entry> entry : compoundMap.entrySet()) { Tag tag = entry.getValue(); String tagValue; switch (tag.getType()) { - case TAG_STRING, TAG_BYTE, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_INT_ARRAY, TAG_LONG, TAG_SHORT, TAG_SHORT_ARRAY, TAG_LONG_ARRAY, TAG_BYTE_ARRAY -> + case TAG_STRING, TAG_BYTE, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_INT_ARRAY, + TAG_LONG, TAG_SHORT, TAG_SHORT_ARRAY, TAG_LONG_ARRAY, TAG_BYTE_ARRAY -> tagValue = tag.getValue().toString(); case TAG_LIST -> { List> list = (List>) tag.getValue(); @@ -105,7 +148,7 @@ private String compoundMapToString(String key, CompoundMap compoundMap) { } case TAG_COMPOUND -> tagValue = compoundMapToString(tag.getName(), (CompoundMap) tag.getValue()); default -> { - continue; + continue; // skip unsupported tag types } } joiner.add("\"" + entry.getKey() + "\":\"" + tagValue + "\""); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java b/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java index d5caf8715..46f9dbd5d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/WriteableRegistry.java @@ -17,7 +17,22 @@ package net.momirealms.customcrops.api.core; +/** + * The WriteableRegistry interface extends the Registry interface, adding the capability + * to register new key-value pairs. This interface is used to define registries that + * allow modifications. + * + * @param the type of the keys used for lookup + * @param the type of the values stored in the registry + */ public interface WriteableRegistry extends Registry { + /** + * Registers a new key-value pair in the registry. + * This method allows adding new entries to the registry dynamically. + * + * @param key the key associated with the value + * @param value the value to be registered + */ void register(K key, T value); -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index ad4a41066..362b22c38 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -409,6 +409,9 @@ private void tickPot(CustomCropsBlockState state, CustomCropsWorld world, Pos updateBlockAppearance(bukkitLocation, config, finalHasNaturalWater, fertilizers(state)); }, bukkitLocation); } + + ActionManager.trigger(Context.block(state) + .arg(ContextKeys.LOCATION, location.toLocation(bukkitWorld)), config.tickActions()); } public int water(CustomCropsBlockState state) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java index e91db6639..29d62d9d1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -249,10 +249,10 @@ private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld worl boolean updateState; if (!config.infinite()) { int water = water(state); - if (water <= 0) { + if (water < config.sprinklingAmount()) { return; } - updateState = water(state, config, water - 1); + updateState = water(state, config, water - config.sprinklingAmount()); } else { updateState = false; } @@ -303,7 +303,7 @@ private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld worl if (anotherState.type() instanceof PotBlock potBlock) { PotConfig potConfig = potBlock.config(anotherState); if (config.potWhitelist().contains(potConfig.id())) { - if (potBlock.addWater(anotherState, potConfig, config.sprinklingAmount())) { + if (potBlock.addWater(anotherState, potConfig, config.wateringAmount())) { BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run( () -> potBlock.updateBlockAppearance( pos3.toLocation(world.bukkitWorld()), diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/BoneMeal.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/BoneMeal.java index 43fa2444c..3ea07e494 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/BoneMeal.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/BoneMeal.java @@ -25,6 +25,9 @@ import java.util.List; +/** + * Represents the configuration and behavior of bone meal in the crop mechanic. + */ public class BoneMeal { private final String item; @@ -35,6 +38,18 @@ public class BoneMeal { private final Action[] actions; private final boolean dispenserAllowed; + /** + * Constructs a new BoneMeal instance with the specified properties. + * + * @param item The identifier for the item required to use this bone meal. + * @param requiredAmount The amount of the required item needed for applying bone meal. + * @param returned The identifier for the item returned after bone meal usage. + * @param returnedAmount The amount of the returned item given back after using bone meal. + * @param dispenserAllowed Whether this bone meal can be used by a dispenser. + * @param pointGainList A list of pairs representing the probability and the corresponding + * points to gain when bone meal is applied. + * @param actions An array of {@link Action} instances to trigger when bone meal is used. + */ public BoneMeal( String item, int requiredAmount, @@ -53,14 +68,30 @@ public BoneMeal( this.dispenserAllowed = dispenserAllowed; } + /** + * Retrieves the identifier of the item required for using this bone meal. + * + * @return The required item identifier. + */ public String requiredItem() { return item; } + /** + * Retrieves the identifier of the item returned after using this bone meal. + * + * @return The returned item identifier. + */ public String returnedItem() { return returned; } + /** + * Randomly determines the points gained from applying the bone meal, + * based on the defined probability and point pairs. + * + * @return The points gained from applying the bone meal. + */ public int rollPoint() { for (Pair pair : pointGainList) { if (Math.random() < pair.left()) { @@ -70,18 +101,38 @@ public int rollPoint() { return 0; } + /** + * Triggers the associated actions when the bone meal is applied. + * + * @param context The context in which the actions are triggered. + */ public void triggerActions(Context context) { ActionManager.trigger(context, actions); } + /** + * Retrieves the amount of the required item needed to use this bone meal. + * + * @return The required item amount. + */ public int amountOfRequiredItem() { return requiredAmount; } + /** + * Retrieves the amount of the returned item after using this bone meal. + * + * @return The returned item amount. + */ public int amountOfReturnItem() { return returnedAmount; } + /** + * Checks if this bone meal can be used by a dispenser. + * + * @return True if the bone meal is dispenser allowed; false otherwise. + */ public boolean isDispenserAllowed() { return dispenserAllowed; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfig.java index c8f409abb..58071b04a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropConfig.java @@ -27,95 +27,333 @@ import java.util.Map; import java.util.Set; +/** + * Represents the configuration settings for a crop in the CustomCrops plugin. + */ public interface CropConfig { + /** + * Gets the unique identifier for this crop configuration. + * + * @return The unique ID of the crop as a {@link String}. + */ String id(); + /** + * Gets the seed item ID associated with this crop. + * + * @return The seed item ID as a {@link String}. + */ String seed(); + /** + * Gets the maximum growth points for this crop. + * + * @return The maximum points as an integer. + */ int maxPoints(); + /** + * Retrieves the requirements that must be met to plant this crop. + * + * @return An array of {@link Requirement} objects for planting the crop. + */ Requirement[] plantRequirements(); + /** + * Retrieves the requirements that must be met to break this crop. + * + * @return An array of {@link Requirement} objects for breaking the crop. + */ Requirement[] breakRequirements(); + /** + * Retrieves the requirements that must be met to interact with this crop. + * + * @return An array of {@link Requirement} objects for interacting with the crop. + */ Requirement[] interactRequirements(); + /** + * Retrieves the growth conditions that must be met for this crop to grow. + * + * @return An array of {@link GrowCondition} objects for crop growth. + */ GrowCondition[] growConditions(); + /** + * Retrieves the actions to be performed when the crop is placed in the wrong pot. + * + * @return An array of {@link Action} objects for wrong pot usage. + */ Action[] wrongPotActions(); + /** + * Retrieves the actions to be performed when interacting with the crop. + * + * @return An array of {@link Action} objects for crop interaction. + */ Action[] interactActions(); + /** + * Retrieves the actions to be performed when breaking the crop. + * + * @return An array of {@link Action} objects for breaking the crop. + */ Action[] breakActions(); + /** + * Retrieves the actions to be performed when planting the crop. + * + * @return An array of {@link Action} objects for planting the crop. + */ Action[] plantActions(); + /** + * Retrieves the actions to be performed when the crop reaches its growth limit. + * + * @return An array of {@link Action} objects for reaching the growth limit. + */ Action[] reachLimitActions(); + /** + * Retrieves the actions to be performed when the crop dies. + * + * @return An array of {@link Action} objects for crop death. + */ Action[] deathActions(); + /** + * Retrieves the conditions that determine when the crop dies. + * + * @return An array of {@link DeathCondition} objects for crop death. + */ DeathCondition[] deathConditions(); + /** + * Retrieves the bone meal effects that can be applied to this crop. + * + * @return An array of {@link BoneMeal} objects representing bone meal effects. + */ BoneMeal[] boneMeals(); + /** + * Indicates whether the crop should consider rotation during placement. + * + * @return True if rotation is considered, false otherwise. + */ boolean rotation(); + /** + * Gets the set of pot IDs that this crop is allowed to be planted in. + * + * @return A set of pot IDs as {@link String} objects. + */ Set potWhitelist(); + /** + * Gets the crop stage configuration based on the growth point value. + * + * @param point The growth points to check. + * @return The {@link CropStageConfig} corresponding to the provided point. + */ CropStageConfig stageByPoint(int point); + /** + * Gets the crop stage configuration based on a stage model identifier. + * + * @param stageModel The stage model identifier. + * @return The {@link CropStageConfig} corresponding to the provided model ID, or null if not found. + */ @Nullable CropStageConfig stageByID(String stageModel); + /** + * Gets the crop stage configuration with a model based on the growth point value. + * + * @param point The growth points to check. + * @return The {@link CropStageConfig} with a model corresponding to the provided point. + */ CropStageConfig stageWithModelByPoint(int point); + /** + * Retrieves all the crop stage configurations for this crop. + * + * @return A collection of {@link CropStageConfig} objects representing all stages. + */ Collection stages(); + /** + * Retrieves all the crop stage IDs for this crop. + * + * @return A collection of stage IDs as {@link String} objects. + */ Collection stageIDs(); + /** + * Gets the closest lower or equal stage configuration based on the provided growth points. + * + * @param previousPoint The growth points to check. + * @return A {@link Map.Entry} containing the point and corresponding {@link CropStageConfig}. + */ Map.Entry getFloorStageEntry(int previousPoint); + /** + * Creates a new builder instance for constructing a {@link CropConfig}. + * + * @return A new {@link Builder} instance. + */ static Builder builder() { return new CropConfigImpl.BuilderImpl(); } + /** + * Builder interface for constructing instances of {@link CropConfig}. + */ interface Builder { + /** + * Builds a new {@link CropConfig} instance with the specified settings. + * + * @return A new {@link CropConfig} instance. + */ CropConfig build(); + /** + * Sets the unique identifier for this crop configuration. + * + * @param id The unique ID of the crop. + * @return The builder instance for chaining. + */ Builder id(String id); + /** + * Sets the seed item ID associated with this crop. + * + * @param seed The seed item ID. + * @return The builder instance for chaining. + */ Builder seed(String seed); + /** + * Sets the maximum growth points for this crop. + * + * @param maxPoints The maximum points the crop can have. + * @return The builder instance for chaining. + */ Builder maxPoints(int maxPoints); + /** + * Sets the actions to be performed when the crop is placed in the wrong pot. + * + * @param wrongPotActions An array of {@link Action} objects for wrong pot usage. + * @return The builder instance for chaining. + */ Builder wrongPotActions(Action[] wrongPotActions); + /** + * Sets the actions to be performed when interacting with the crop. + * + * @param interactActions An array of {@link Action} objects for crop interaction. + * @return The builder instance for chaining. + */ Builder interactActions(Action[] interactActions); + /** + * Sets the actions to be performed when breaking the crop. + * + * @param breakActions An array of {@link Action} objects for breaking the crop. + * @return The builder instance for chaining. + */ Builder breakActions(Action[] breakActions); + /** + * Sets the actions to be performed when planting the crop. + * + * @param plantActions An array of {@link Action} objects for planting the crop. + * @return The builder instance for chaining. + */ Builder plantActions(Action[] plantActions); + /** + * Sets the actions to be performed when the crop reaches its growth limit. + * + * @param reachLimitActions An array of {@link Action} objects for reaching the growth limit. + * @return The builder instance for chaining. + */ Builder reachLimitActions(Action[] reachLimitActions); + /** + * Sets the requirements that must be met to plant this crop. + * + * @param plantRequirements An array of {@link Requirement} objects for planting the crop. + * @return The builder instance for chaining. + */ Builder plantRequirements(Requirement[] plantRequirements); + /** + * Sets the requirements that must be met to break this crop. + * + * @param breakRequirements An array of {@link Requirement} objects for breaking the crop. + * @return The builder instance for chaining. + */ Builder breakRequirements(Requirement[] breakRequirements); + /** + * Sets the requirements that must be met to interact with this crop. + * + * @param interactRequirements An array of {@link Requirement} objects for interacting with the crop. + * @return The builder instance for chaining. + */ Builder interactRequirements(Requirement[] interactRequirements); + /** + * Sets the growth conditions that must be met for this crop to grow. + * + * @param growConditions An array of {@link GrowCondition} objects for crop growth. + * @return The builder instance for chaining. + */ Builder growConditions(GrowCondition[] growConditions); + /** + * Sets the conditions that determine when the crop dies. + * + * @param deathConditions An array of {@link DeathCondition} objects for crop death. + * @return The builder instance for chaining. + */ Builder deathConditions(DeathCondition[] deathConditions); + /** + * Sets the bone meal effects that can be applied to this crop. + * + * @param boneMeals An array of {@link BoneMeal} objects representing bone meal effects. + * @return The builder instance for chaining. + */ Builder boneMeals(BoneMeal[] boneMeals); + /** + * Sets whether the crop should consider rotation during placement. + * + * @param rotation True if rotation is considered, false otherwise. + * @return The builder instance for chaining. + */ Builder rotation(boolean rotation); + /** + * Sets the pot whitelist for this crop. + * Only pots in this list will be allowed to plant the crop. + * + * @param whitelist A set of pot IDs that are allowed to plant this crop. + * @return The builder instance for chaining. + */ Builder potWhitelist(Set whitelist); + /** + * Sets the stages of the crop based on growth points. + * + * @param stages A collection of {@link CropStageConfig.Builder} objects representing crop stages. + * @return The builder instance for chaining. + */ Builder stages(Collection stages); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfig.java index 3639c54bf..b9c103877 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/CropStageConfig.java @@ -24,55 +24,183 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; +/** + * Interface representing the configuration of a specific stage of crop growth. + */ public interface CropStageConfig { + /** + * Gets the parent {@link CropConfig} associated with this crop stage. + * + * @return The parent crop configuration. + */ CropConfig crop(); + /** + * Gets the offset for displaying crop information. + * This offset is used to adjust the display of information related to the crop stage. + * + * @return The display information offset. + */ double displayInfoOffset(); + /** + * Gets the unique identifier for this crop stage. + * + * @return The stage ID, or null if not defined. + */ @Nullable String stageID(); + /** + * Gets the growth point associated with this crop stage. + * This point represents the growth progress of the crop. + * + * @return The growth point. + */ int point(); + /** + * Gets the requirements that must be met to interact with the crop at this stage. + * + * @return An array of interaction requirements. + */ Requirement[] interactRequirements(); + /** + * Gets the requirements that must be met to break the crop at this stage. + * + * @return An array of break requirements. + */ Requirement[] breakRequirements(); + /** + * Gets the actions to be performed when interacting with the crop at this stage. + * + * @return An array of interaction actions. + */ Action[] interactActions(); + /** + * Gets the actions to be performed when breaking the crop at this stage. + * + * @return An array of break actions. + */ Action[] breakActions(); + /** + * Gets the actions to be performed when the crop grows to this stage. + * + * @return An array of grow actions. + */ Action[] growActions(); + /** + * Gets the form of existence that this crop stage takes. + * + * @return The {@link ExistenceForm} of the crop stage. + */ ExistenceForm existenceForm(); + /** + * Creates a new builder for constructing instances of {@link CropStageConfig}. + * + * @return A new {@link Builder} instance. + */ static Builder builder() { return new CropStageConfigImpl.BuilderImpl(); } + /** + * Builder interface for constructing instances of {@link CropStageConfig}. + */ interface Builder { + /** + * Builds a new {@link CropStageConfig} instance with the specified settings. + * + * @return A new {@link CropStageConfig} instance. + */ CropStageConfig build(); + /** + * Sets the parent crop configuration for this crop stage. + * + * @param crop The parent {@link CropConfig}. + * @return The builder instance for chaining. + */ Builder crop(CropConfig crop); + /** + * Sets the display information offset for this crop stage. + * + * @param offset The display info offset. + * @return The builder instance for chaining. + */ Builder displayInfoOffset(double offset); + /** + * Sets the unique identifier for this crop stage. + * + * @param id The stage ID. + * @return The builder instance for chaining. + */ Builder stageID(String id); + /** + * Sets the growth point associated with this crop stage. + * + * @param i The growth point. + * @return The builder instance for chaining. + */ Builder point(int i); + /** + * Sets the interaction requirements for this crop stage. + * + * @param requirements An array of interaction requirements. + * @return The builder instance for chaining. + */ Builder interactRequirements(Requirement[] requirements); + /** + * Sets the break requirements for this crop stage. + * + * @param requirements An array of break requirements. + * @return The builder instance for chaining. + */ Builder breakRequirements(Requirement[] requirements); + /** + * Sets the interaction actions for this crop stage. + * + * @param actions An array of interaction actions. + * @return The builder instance for chaining. + */ Builder interactActions(Action[] actions); + /** + * Sets the break actions for this crop stage. + * + * @param actions An array of break actions. + * @return The builder instance for chaining. + */ Builder breakActions(Action[] actions); + /** + * Sets the grow actions for this crop stage. + * + * @param actions An array of grow actions. + * @return The builder instance for chaining. + */ Builder growActions(Action[] actions); + /** + * Sets the existence form of the crop for this crop stage. + * + * @param existenceForm The {@link ExistenceForm} of the crop. + * @return The builder instance for chaining. + */ Builder existenceForm(ExistenceForm existenceForm); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/DeathCondition.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/DeathCondition.java index ebcdeec5d..abdc5c76f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/DeathCondition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/DeathCondition.java @@ -24,6 +24,10 @@ import net.momirealms.customcrops.api.requirement.RequirementManager; import org.jetbrains.annotations.Nullable; + +/** + * Represents a condition that determines whether a crop will die. + */ public class DeathCondition { private final Requirement[] requirements; @@ -31,6 +35,14 @@ public class DeathCondition { private final ExistenceForm existenceForm; private final int deathDelay; + /** + * Constructs a new DeathCondition with the specified requirements, death stage, existence form, and death delay. + * + * @param requirements The array of {@link Requirement} instances that must be met for the crop to die. + * @param deathStage The stage ID to transition to when the crop dies. Can be null if there is no specific death stage. + * @param existenceForm The {@link ExistenceForm} representing the state of the crop after death. + * @param deathDelay The delay in ticks before the crop transitions to the death stage after the condition is met. + */ public DeathCondition(Requirement[] requirements, String deathStage, ExistenceForm existenceForm, int deathDelay) { this.requirements = requirements; this.deathStage = deathStage; @@ -38,20 +50,43 @@ public DeathCondition(Requirement[] requirements, String this.deathDelay = deathDelay; } + /** + * Retrieves the stage ID to transition to upon death. + * + * @return The stage ID for the death state, or null if there is no specific death stage. + */ @Nullable public String deathStage() { return deathStage; } + /** + * Retrieves the delay in ticks before transitioning to the death stage. + * + * @return The delay before the crop dies, in ticks. + */ public int deathDelay() { return deathDelay; } + /** + * Checks if the death condition is met in the given context. + * This method evaluates all the requirements associated with this condition to determine + * whether the crop should die. + * + * @param context The {@link Context} in which the requirements are evaluated. + * @return True if all requirements are satisfied; false otherwise. + */ public boolean isMet(Context context) { return RequirementManager.isSatisfied(context, requirements); } + /** + * Retrieves the existence form that the crop should take after it dies. + * + * @return The {@link ExistenceForm} representing the state of the crop after death. + */ public ExistenceForm existenceForm() { return existenceForm; } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/GrowCondition.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/GrowCondition.java index a3277ed51..21b658263 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/GrowCondition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/GrowCondition.java @@ -22,21 +22,45 @@ import net.momirealms.customcrops.api.requirement.Requirement; import net.momirealms.customcrops.api.requirement.RequirementManager; +/** + * Represents a condition for a crop to grow to the next stage. + * The growth condition specifies the requirements that must be met for the crop to progress, + * as well as the number of points to be added to the crop's growth when the condition is met. + */ public class GrowCondition { private final Requirement[] requirements; private final int pointToAdd; + /** + * Constructs a new GrowCondition with the specified requirements and growth points. + * + * @param requirements The array of {@link Requirement} instances that must be met for the crop to grow. + * @param pointToAdd The number of points to be added to the crop's growth when the condition is satisfied. + */ public GrowCondition(Requirement[] requirements, int pointToAdd) { this.requirements = requirements; this.pointToAdd = pointToAdd; } + /** + * Retrieves the number of growth points to be added when this condition is met. + * + * @return The number of points to add to the crop's growth. + */ public int pointToAdd() { return pointToAdd; } + /** + * Checks if the growth condition is met in the given context. + * This method evaluates all the requirements associated with this condition to determine + * whether the crop can grow. + * + * @param context The {@link Context} in which the requirements are evaluated. + * @return True if all requirements are satisfied; false otherwise. + */ public boolean isMet(Context context) { return RequirementManager.isSatisfied(context, requirements); } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/VariationData.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/VariationData.java index 4d1d3ab6e..a67e4f9ca 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/VariationData.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/crop/VariationData.java @@ -19,26 +19,51 @@ import net.momirealms.customcrops.api.core.ExistenceForm; +/** + * Represents the data associated with a crop variation. + */ public class VariationData { private final String id; private final ExistenceForm form; private final double chance; + /** + * Constructs a new VariationData with the specified ID, existence form, and chance. + * + * @param id The unique identifier for the crop variation. + * @param form The {@link ExistenceForm} representing the state or form of the crop variation. + * @param chance The probability (as a decimal between 0 and 1) of this variation occurring. + */ public VariationData(String id, ExistenceForm form, double chance) { this.id = id; this.form = form; this.chance = chance; } + /** + * Retrieves the unique identifier for this crop variation. + * + * @return The unique ID of the variation. + */ public String id() { return id; } + /** + * Retrieves the existence form of this crop variation. + * + * @return The {@link ExistenceForm} representing the state or form of the variation. + */ public ExistenceForm existenceForm() { return form; } + /** + * Retrieves the chance of this crop variation occurring. + * + * @return The probability of the variation occurring, as a decimal between 0 and 1. + */ public double chance() { return chance; } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Fertilizer.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Fertilizer.java index e17da6d77..a7ab4c685 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Fertilizer.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/Fertilizer.java @@ -20,15 +20,40 @@ import net.momirealms.customcrops.api.core.Registries; import org.jetbrains.annotations.Nullable; +/** + * Represents a fertilizer used in the CustomCrops plugin. + */ public interface Fertilizer { + /** + * Gets the unique identifier of the fertilizer. + * + * @return The fertilizer ID as a {@link String}. + */ String id(); + /** + * Gets the remaining number of times this fertilizer can be used. + * + * @return The number of remaining usages. + */ int times(); + /** + * Reduces the usage times of the fertilizer by one. + * If the number of usages reaches zero or below, it indicates the fertilizer is exhausted. + * + * @return True if the fertilizer is exhausted (no more usages left), false otherwise. + */ boolean reduceTimes(); - // Flexibility matters more than performance + /** + * Retrieves the type of the fertilizer. + * This method provides flexibility in determining the type by querying the fertilizer registry. + * It may incur a slight performance cost due to registry lookup. + * + * @return The {@link FertilizerType} of this fertilizer. Returns {@link FertilizerType#INVALID} if the type is not found. + */ default FertilizerType type() { FertilizerConfig config = Registries.FERTILIZER.get(id()); if (config == null) { @@ -37,21 +62,52 @@ default FertilizerType type() { return config.type(); } + /** + * Retrieves the configuration of the fertilizer from the registry. + * This method allows access to additional properties and settings of the fertilizer. + * + * @return The {@link FertilizerConfig} associated with this fertilizer, or null if not found. + */ @Nullable default FertilizerConfig config() { return Registries.FERTILIZER.get(id()); } + /** + * Creates a new builder instance for constructing a {@link Fertilizer}. + * + * @return A new {@link Builder} instance. + */ static Builder builder() { return new FertilizerImpl.BuilderImpl(); } + /** + * Builder interface for constructing instances of {@link Fertilizer}. + */ interface Builder { + /** + * Builds a new {@link Fertilizer} instance with the specified settings. + * + * @return A new {@link Fertilizer} instance. + */ Fertilizer build(); + /** + * Sets the unique identifier for the fertilizer. + * + * @param id The unique ID for the fertilizer. + * @return The current instance of the Builder. + */ Builder id(String id); + /** + * Sets the number of times the fertilizer can be used. + * + * @param times The number of usages for the fertilizer. + * @return The current instance of the Builder. + */ Builder times(int times); } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerConfig.java index affce2119..27c3793ac 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/fertilizer/FertilizerConfig.java @@ -24,38 +24,126 @@ import java.util.Set; +/** + * Represents the configuration settings for a fertilizer in the CustomCrops plugin. + */ public interface FertilizerConfig { + /** + * Gets the unique identifier for this fertilizer configuration. + * + * @return The unique ID of the fertilizer as a {@link String}. + */ String id(); + /** + * Gets the type of the fertilizer. + * + * @return The {@link FertilizerType} of this fertilizer. + */ FertilizerType type(); + /** + * Determines whether the fertilizer should be used before planting crops. + * + * @return True if the fertilizer is applied before planting, false otherwise. + */ boolean beforePlant(); + /** + * Gets the icon representation for this fertilizer. + * + * @return The icon as a {@link String}, typically an item or block ID. + */ String icon(); + /** + * Retrieves the requirements for using this fertilizer. + * + * @return An array of {@link Requirement} objects that must be met to use the fertilizer. + */ Requirement[] requirements(); + /** + * Gets the item ID associated with this fertilizer. + * + * @return The item ID as a {@link String}. + */ String itemID(); + /** + * Gets the number of times this fertilizer can be used. + * + * @return The number of usages available. + */ int times(); + /** + * Gets the set of pot IDs that this fertilizer is allowed to be used with. + * + * @return A set of pot IDs as {@link String} objects. + */ Set whitelistPots(); + /** + * Retrieves the actions to be performed before planting when using this fertilizer. + * + * @return An array of {@link Action} objects representing the actions to perform. + */ Action[] beforePlantActions(); + /** + * Retrieves the actions to be performed when the fertilizer is used. + * + * @return An array of {@link Action} objects representing the actions to perform upon use. + */ Action[] useActions(); + /** + * Retrieves the actions to be performed when the fertilizer is used on the wrong type of pot. + * + * @return An array of {@link Action} objects representing the actions to perform for wrong pot usage. + */ Action[] wrongPotActions(); + /** + * Processes and potentially modifies the gain points based on this fertilizer's effects. + * + * @param previousPoints The points before processing. + * @return The modified points after applying the fertilizer's effect. + */ int processGainPoints(int previousPoints); + /** + * Processes and potentially modifies the amount of water to lose when this fertilizer is used. + * + * @param waterToLose The amount of water to lose before processing. + * @return The modified water loss amount after applying the fertilizer's effect. + */ int processWaterToLose(int waterToLose); + /** + * Processes and potentially modifies the variation chance for crop growth based on this fertilizer's effects. + * + * @param previousChance The variation chance before processing. + * @return The modified variation chance after applying the fertilizer's effect. + */ double processVariationChance(double previousChance); + /** + * Processes and potentially modifies the amount of items dropped when the crop is harvested. + * + * @param amount The amount of items to drop before processing. + * @return The modified drop amount after applying the fertilizer's effect. + */ int processDroppedItemAmount(int amount); + /** + * Provides an optional override for the quality ratio of crops affected by this fertilizer. + * This method may return null if there is no override. + * + * @return An array of doubles representing the quality ratio override, or null if not applicable. + */ @Nullable double[] overrideQualityRatio(); -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfig.java index 2fd0794bf..816a31fca 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/pot/PotConfig.java @@ -29,96 +29,340 @@ import java.util.HashMap; import java.util.Set; +/** + * Represents the configuration settings for a pot in the CustomCrops plugin. + */ public interface PotConfig { + /** + * Gets the unique identifier for this pot configuration. + * + * @return The ID of the pot configuration. + */ String id(); + /** + * Gets the maximum water storage capacity of the pot. + * + * @return The water storage capacity. + */ int storage(); + /** + * Checks if the pot can accept rain as a source of water. + * + * @return True if rain is accepted, false otherwise. + */ boolean isRainDropAccepted(); + /** + * Checks if the pot can accept water from nearby water sources. + * + * @return True if nearby water is accepted, false otherwise. + */ boolean isNearbyWaterAccepted(); + /** + * Gets the methods available for watering the pot. + * + * @return An array of {@link WateringMethod} instances. + */ WateringMethod[] wateringMethods(); + /** + * Gets the set of block IDs associated with this pot. + * + * @return A set of block IDs. + */ Set blocks(); + /** + * Checks if a specific block ID is considered wet. + * + * @param blockID The block ID to check. + * @return True if the block is wet, false otherwise. + */ boolean isWet(String blockID); + /** + * Gets the water bar that manages the pot's water level. + * + * @return The {@link WaterBar} instance. + */ WaterBar waterBar(); + /** + * Gets the maximum number of fertilizers that can be added to the pot. + * + * @return The maximum number of fertilizers. + */ int maxFertilizers(); + /** + * Gets the appearance of the pot based on its water state and fertilizer type. + * + * @param watered Whether the pot is watered. + * @param type The fertilizer type. + * @return The appearance ID of the pot. + */ String getPotAppearance(boolean watered, FertilizerType type); + /** + * Gets the requirements that must be met to place the pot. + * + * @return An array of {@link Requirement} instances for placement. + */ Requirement[] placeRequirements(); + /** + * Gets the requirements that must be met to break the pot. + * + * @return An array of {@link Requirement} instances for breaking. + */ Requirement[] breakRequirements(); + /** + * Gets the requirements that must be met to use the pot. + * + * @return An array of {@link Requirement} instances for usage. + */ Requirement[] useRequirements(); + /** + * Gets the actions performed during each tick of the pot. + * + * @return An array of tick {@link Action} instances. + */ Action[] tickActions(); + /** + * Gets the actions performed when the pot reaches its limit. + * + * @return An array of reach limit {@link Action} instances. + */ Action[] reachLimitActions(); + /** + * Gets the actions performed when the pot is interacted with. + * + * @return An array of interact {@link Action} instances. + */ Action[] interactActions(); + /** + * Gets the actions performed when the pot is placed. + * + * @return An array of place {@link Action} instances. + */ Action[] placeActions(); + /** + * Gets the actions performed when the pot is broken. + * + * @return An array of break {@link Action} instances. + */ Action[] breakActions(); + /** + * Gets the actions performed when water is added to the pot. + * + * @return An array of add water {@link Action} instances. + */ Action[] addWaterActions(); + /** + * Gets the actions performed when the pot is full of water. + * + * @return An array of full water {@link Action} instances. + */ Action[] fullWaterActions(); + /** + * Gets the actions performed when the pot reaches its maximum fertilizer capacity. + * + * @return An array of max fertilizer {@link Action} instances. + */ Action[] maxFertilizerActions(); + /** + * Creates a new builder instance for constructing a {@link PotConfig}. + * + * @return A new {@link Builder} instance. + */ static Builder builder() { return new PotConfigImpl.BuilderImpl(); } + /** + * Builder interface for constructing instances of {@link PotConfig}. + */ interface Builder { + /** + * Builds a new {@link PotConfig} instance with the specified settings. + * + * @return A new {@link PotConfig} instance. + */ PotConfig build(); + /** + * Sets the unique identifier for the pot configuration. + * + * @param id The unique ID for the pot. + * @return The current instance of the Builder. + */ Builder id(String id); + /** + * Sets the maximum water storage capacity of the pot. + * + * @param storage The maximum amount of water the pot can store. + * @return The current instance of the Builder. + */ Builder storage(int storage); + /** + * Sets whether the pot can accept rain as a source of water. + * + * @param isRainDropAccepted True if rain is accepted, false otherwise. + * @return The current instance of the Builder. + */ Builder isRainDropAccepted(boolean isRainDropAccepted); + /** + * Sets whether the pot can accept water from nearby water sources. + * + * @param isNearbyWaterAccepted True if nearby water is accepted, false otherwise. + * @return The current instance of the Builder. + */ Builder isNearbyWaterAccepted(boolean isNearbyWaterAccepted); + /** + * Sets the methods available for watering the pot. + * + * @param wateringMethods An array of {@link WateringMethod} instances. + * @return The current instance of the Builder. + */ Builder wateringMethods(WateringMethod[] wateringMethods); + /** + * Sets the water bar that indicates the pot's current water level. + * + * @param waterBar The {@link WaterBar} instance. + * @return The current instance of the Builder. + */ Builder waterBar(WaterBar waterBar); + /** + * Sets the maximum number of fertilizers that can be added to the pot. + * + * @param maxFertilizers The maximum number of fertilizers. + * @return The current instance of the Builder. + */ Builder maxFertilizers(int maxFertilizers); + /** + * Sets the requirements that must be met to place the pot. + * + * @param requirements An array of {@link Requirement} instances. + * @return The current instance of the Builder. + */ Builder placeRequirements(Requirement[] requirements); + /** + * Sets the requirements that must be met to break the pot. + * + * @param requirements An array of {@link Requirement} instances. + * @return The current instance of the Builder. + */ Builder breakRequirements(Requirement[] requirements); + /** + * Sets the requirements that must be met to use the pot. + * + * @param requirements An array of {@link Requirement} instances. + * @return The current instance of the Builder. + */ Builder useRequirements(Requirement[] requirements); + /** + * Sets the actions performed during each tick of the pot. + * + * @param tickActions An array of tick {@link Action} instances. + * @return The current instance of the Builder. + */ Builder tickActions(Action[] tickActions); + /** + * Sets the actions performed when the pot reaches its limit. + * + * @param reachLimitActions An array of reach limit {@link Action} instances. + * @return The current instance of the Builder. + */ Builder reachLimitActions(Action[] reachLimitActions); + /** + * Sets the actions performed when the pot is interacted with. + * + * @param interactActions An array of interact {@link Action} instances. + * @return The current instance of the Builder. + */ Builder interactActions(Action[] interactActions); + /** + * Sets the actions performed when the pot is placed. + * + * @param placeActions An array of place {@link Action} instances. + * @return The current instance of the Builder. + */ Builder placeActions(Action[] placeActions); + /** + * Sets the actions performed when the pot is broken. + * + * @param breakActions An array of break {@link Action} instances. + * @return The current instance of the Builder. + */ Builder breakActions(Action[] breakActions); + /** + * Sets the actions performed when water is added to the pot. + * + * @param addWaterActions An array of add water {@link Action} instances. + * @return The current instance of the Builder. + */ Builder addWaterActions(Action[] addWaterActions); + /** + * Sets the actions performed when the pot is full of water. + * + * @param fullWaterActions An array of full water {@link Action} instances. + * @return The current instance of the Builder. + */ Builder fullWaterActions(Action[] fullWaterActions); + /** + * Sets the actions performed when the pot reaches its maximum fertilizer capacity. + * + * @param maxFertilizerActions An array of max fertilizer {@link Action} instances. + * @return The current instance of the Builder. + */ Builder maxFertilizerActions(Action[] maxFertilizerActions); + /** + * Sets the basic appearance of the pot. + * + * @param basicAppearance A {@link Pair} representing the basic appearance of the pot. + * @return The current instance of the Builder. + */ Builder basicAppearance(Pair basicAppearance); + /** + * Sets the appearance map of the pot for different fertilizer types. + * + * @param potAppearanceMap A map of {@link FertilizerType} to {@link Pair} of appearance strings. + * @return The current instance of the Builder. + */ Builder potAppearanceMap(HashMap> potAppearanceMap); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfig.java index 52195b183..73f857003 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfig.java @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package net.momirealms.customcrops.api.core.mechanic.sprinkler; import net.momirealms.customcrops.api.action.Action; @@ -29,137 +28,400 @@ import java.util.Set; +/** + * Represents the configuration settings for a sprinkler within the CustomCrops plugin. + */ public interface SprinklerConfig { + /** + * Gets the unique identifier for this sprinkler configuration. + * + * @return The ID of the sprinkler configuration. + */ String id(); + /** + * Gets the maximum water storage capacity of the sprinkler. + * + * @return The water storage capacity. + */ int storage(); + /** + * Gets the range of the sprinkler's watering area. + * + * @return A 2D array representing the offsets of the target blocks. + */ int[][] range(); + /** + * Checks if the sprinkler has infinite water capacity. + * + * @return true if the sprinkler is infinite, false otherwise. + */ boolean infinite(); + /** + * Gets the amount of water the sprinkler adds to pot per operation. + * + * @return The sprinkling amount. + */ + int wateringAmount(); + + /** + * Gets the amount of water sprinkled per operation. + * + * @return The sprinkling amount. + */ int sprinklingAmount(); + /** + * Gets the 2D item representation of the sprinkler. + * + * @return The 2D item ID, or null if not applicable. + */ @Nullable String twoDItem(); + /** + * Gets the 3D item representation of the sprinkler without water. + * + * @return The 3D item ID without water. + */ @NotNull String threeDItem(); + /** + * Gets the 3D item representation of the sprinkler with water. + * + * @return The 3D item ID with water. + */ @NotNull String threeDItemWithWater(); + /** + * Gets the whitelist of pots that the sprinkler can interact with. + * + * @return A set of pot IDs that are whitelisted. + */ @NotNull Set potWhitelist(); + /** + * Gets the model IDs associated with this sprinkler. + * + * @return A set of model IDs. + */ @NotNull Set modelIDs(); + /** + * Gets the water bar that manages the sprinkler's water level. + * + * @return The {@link WaterBar}, or null if not applicable. + */ @Nullable WaterBar waterBar(); + /** + * Gets the existence form of the sprinkler, which indicates how it exists in the world. + * + * @return The {@link ExistenceForm} of the sprinkler. + */ @NotNull ExistenceForm existenceForm(); /** - * Get the requirements for placement + * Gets the placement requirements for the sprinkler. * - * @return requirements for placement + * @return An array of placement {@link Requirement}s, or null if none. */ @Nullable Requirement[] placeRequirements(); /** - * Get the requirements for breaking + * Gets the breaking requirements for the sprinkler. * - * @return requirements for breaking + * @return An array of breaking {@link Requirement}s, or null if none. */ @Nullable Requirement[] breakRequirements(); /** - * Get the requirements for using + * Gets the usage requirements for the sprinkler. * - * @return requirements for using + * @return An array of usage {@link Requirement}s, or null if none. */ @Nullable Requirement[] useRequirements(); + /** + * Gets the actions performed when the sprinkler is working. + * + * @return An array of work {@link Action}s, or null if none. + */ @Nullable Action[] workActions(); + /** + * Gets the actions performed when the sprinkler is interacted with. + * + * @return An array of interact {@link Action}s, or null if none. + */ @Nullable Action[] interactActions(); + /** + * Gets the actions performed when the sprinkler is placed. + * + * @return An array of place {@link Action}s, or null if none. + */ @Nullable Action[] placeActions(); + /** + * Gets the actions performed when the sprinkler is broken. + * + * @return An array of break {@link Action}s, or null if none. + */ @Nullable Action[] breakActions(); + /** + * Gets the actions performed when water is added to the sprinkler. + * + * @return An array of add water {@link Action}s, or null if none. + */ @Nullable Action[] addWaterActions(); + /** + * Gets the actions performed when the sprinkler reaches its water limit. + * + * @return An array of reach limit {@link Action}s, or null if none. + */ @Nullable Action[] reachLimitActions(); + /** + * Gets the actions performed when the sprinkler's water level is full. + * + * @return An array of full water {@link Action}s, or null if none. + */ @Nullable Action[] fullWaterActions(); + /** + * Gets the methods used for watering by the sprinkler. + * + * @return An array of {@link WateringMethod}s. + */ @NotNull WateringMethod[] wateringMethods(); + /** + * Creates a new builder instance for constructing a {@link SprinklerConfig}. + * + * @return A new {@link Builder} instance. + */ static Builder builder() { return new SprinklerConfigImpl.BuilderImpl(); } + /** + * Builder interface for constructing instances of {@link SprinklerConfig}. + */ interface Builder { + /** + * Builds a new {@link SprinklerConfig} instance with the specified settings. + * + * @return A new {@link SprinklerConfig} instance. + */ SprinklerConfig build(); + /** + * Sets the unique identifier for the sprinkler configuration. + * + * @param id The unique ID for the sprinkler. + * @return The current instance of the Builder. + */ Builder id(String id); + /** + * Sets the existence form of the sprinkler, indicating its form in the world. + * + * @param existenceForm The existence form of the sprinkler. + * @return The current instance of the Builder. + */ Builder existenceForm(ExistenceForm existenceForm); + /** + * Sets the water storage capacity of the sprinkler. + * + * @param storage The maximum water storage capacity. + * @return The current instance of the Builder. + */ Builder storage(int storage); + /** + * Sets the range of the sprinkler's watering area. + * + * @param range A 2D array defining the range. + * @return The current instance of the Builder. + */ Builder range(int[][] range); + /** + * Specifies whether the sprinkler has an infinite water supply. + * + * @param infinite true if the sprinkler has infinite water; false otherwise. + * @return The current instance of the Builder. + */ Builder infinite(boolean infinite); + /** + * Sets the amount of water the sprinkler adds to pot per operation. + * + * @param wateringAmount The amount of water used per sprinkling. + * @return The current instance of the Builder. + */ + Builder wateringAmount(int wateringAmount); + + /** + * Sets the amount of water the sprinkler uses per operation. + * + * @param sprinklingAmount The amount of water used per sprinkling. + * @return The current instance of the Builder. + */ Builder sprinklingAmount(int sprinklingAmount); + /** + * Sets the whitelist of pot IDs that the sprinkler can interact with. + * + * @param potWhitelist A set of whitelisted pot IDs. + * @return The current instance of the Builder. + */ Builder potWhitelist(Set potWhitelist); + /** + * Sets the water bar that manages the sprinkler's water level. + * + * @param waterBar The {@link WaterBar} instance. + * @return The current instance of the Builder. + */ Builder waterBar(WaterBar waterBar); + /** + * Sets the 2D item representation of the sprinkler. + * + * @param twoDItem The ID of the 2D item representation. + * @return The current instance of the Builder. + */ Builder twoDItem(@Nullable String twoDItem); + /** + * Sets the 3D item representation of the sprinkler when it does not contain water. + * + * @param threeDItem The ID of the 3D item without water. + * @return The current instance of the Builder. + */ Builder threeDItem(String threeDItem); + /** + * Sets the 3D item representation of the sprinkler when it contains water. + * + * @param threeDItemWithWater The ID of the 3D item with water. + * @return The current instance of the Builder. + */ Builder threeDItemWithWater(String threeDItemWithWater); + /** + * Sets the requirements for placing the sprinkler. + * + * @param placeRequirements An array of {@link Requirement} instances for placement. + * @return The current instance of the Builder. + */ Builder placeRequirements(Requirement[] placeRequirements); + /** + * Sets the requirements for breaking the sprinkler. + * + * @param breakRequirements An array of {@link Requirement} instances for breaking. + * @return The current instance of the Builder. + */ Builder breakRequirements(Requirement[] breakRequirements); + /** + * Sets the requirements for using the sprinkler. + * + * @param useRequirements An array of {@link Requirement} instances for usage. + * @return The current instance of the Builder. + */ Builder useRequirements(Requirement[] useRequirements); + /** + * Sets the actions to be performed when the sprinkler is working. + * + * @param workActions An array of {@link Action} instances for working. + * @return The current instance of the Builder. + */ Builder workActions(Action[] workActions); + /** + * Sets the actions to be performed when the sprinkler is interacted with. + * + * @param interactActions An array of {@link Action} instances for interaction. + * @return The current instance of the Builder. + */ Builder interactActions(Action[] interactActions); + /** + * Sets the actions to be performed when water is added to the sprinkler. + * + * @param addWaterActions An array of {@link Action} instances for adding water. + * @return The current instance of the Builder. + */ Builder addWaterActions(Action[] addWaterActions); + /** + * Sets the actions to be performed when the sprinkler reaches its water limit. + * + * @param reachLimitActions An array of {@link Action} instances for reaching the limit. + * @return The current instance of the Builder. + */ Builder reachLimitActions(Action[] reachLimitActions); + /** + * Sets the actions to be performed when the sprinkler is placed. + * + * @param placeActions An array of {@link Action} instances for placing. + * @return The current instance of the Builder. + */ Builder placeActions(Action[] placeActions); + /** + * Sets the actions to be performed when the sprinkler is broken. + * + * @param breakActions An array of {@link Action} instances for breaking. + * @return The current instance of the Builder. + */ Builder breakActions(Action[] breakActions); + /** + * Sets the actions to be performed when the sprinkler's water level is full. + * + * @param fullWaterActions An array of {@link Action} instances for full water. + * @return The current instance of the Builder. + */ Builder fullWaterActions(Action[] fullWaterActions); + /** + * Sets the watering methods that the sprinkler can use. + * + * @param wateringMethods An array of {@link WateringMethod} instances. + * @return The current instance of the Builder. + */ Builder wateringMethods(WateringMethod[] wateringMethods); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfigImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfigImpl.java index 0a4d1bcc5..d232768da 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfigImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/sprinkler/SprinklerConfigImpl.java @@ -36,6 +36,7 @@ public class SprinklerConfigImpl implements SprinklerConfig { private final int storage; private final int[][] range; private final boolean infinite; + private final int wateringAmount; private final int sprinklingAmount; private final Set potWhitelist; private final WaterBar waterBar; @@ -61,6 +62,7 @@ public SprinklerConfigImpl( int storage, int[][] range, boolean infinite, + int wateringAmount, int sprinklingAmount, Set potWhitelist, WaterBar waterBar, @@ -84,6 +86,7 @@ public SprinklerConfigImpl( this.storage = storage; this.range = range; this.infinite = infinite; + this.wateringAmount = wateringAmount; this.sprinklingAmount = sprinklingAmount; this.potWhitelist = potWhitelist; this.waterBar = waterBar; @@ -126,6 +129,11 @@ public boolean infinite() { return infinite; } + @Override + public int wateringAmount() { + return wateringAmount; + } + @Override public int sprinklingAmount() { return sprinklingAmount; @@ -233,6 +241,7 @@ public static class BuilderImpl implements Builder { private int storage; private int[][] range; private boolean infinite; + private int wateringAmount; private int sprinklingAmount; private Set potWhitelist; private WaterBar waterBar; @@ -253,7 +262,7 @@ public static class BuilderImpl implements Builder { @Override public SprinklerConfig build() { - return new SprinklerConfigImpl(id, existenceForm, storage, range, infinite, sprinklingAmount, potWhitelist, waterBar, twoDItem, threeDItem, threeDItemWithWater, + return new SprinklerConfigImpl(id, existenceForm, storage, range, infinite, wateringAmount, sprinklingAmount, potWhitelist, waterBar, twoDItem, threeDItem, threeDItemWithWater, placeRequirements, breakRequirements, useRequirements, workActions, interactActions, reachLimitActions, addWaterActions, placeActions, breakActions, fullWaterActions, wateringMethods); } @@ -287,6 +296,12 @@ public Builder infinite(boolean infinite) { return this; } + @Override + public Builder wateringAmount(int wateringAmount) { + this.wateringAmount = wateringAmount; + return this; + } + @Override public Builder sprinklingAmount(int sprinklingAmount) { this.sprinklingAmount = sprinklingAmount; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfig.java b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfig.java index ba51753f7..f0cb4a796 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfig.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/mechanic/wateringcan/WateringCanConfig.java @@ -28,98 +28,345 @@ import java.util.Map; import java.util.Set; +/** + * Represents the configuration settings for a watering can in the CustomCrops plugin + */ public interface WateringCanConfig { + /** + * Gets the unique identifier for this watering can configuration. + * + * @return The ID of the watering can configuration. + */ String id(); + /** + * Gets the item ID representing the watering can. + * + * @return The item ID. + */ String itemID(); + /** + * Gets the width of the watering area. + * + * @return The width of the watering area. + */ int width(); + /** + * Gets the length of the watering area. + * + * @return The length of the watering area. + */ int length(); + /** + * Gets the maximum water storage capacity of the watering can. + * + * @return The water storage capacity. + */ int storage(); + /** + * Gets the amount of water added to pots per watering action. + * + * @return The watering amount. + */ int wateringAmount(); + /** + * Determines if the lore (description text) of the watering can is dynamic. + * + * @return true if the lore is dynamic, false otherwise. + */ boolean dynamicLore(); + /** + * Gets the set of pot IDs that the watering can is whitelisted to interact with. + * + * @return A set of whitelisted pot IDs. + */ Set whitelistPots(); + /** + * Gets the set of sprinkler IDs that the watering can is whitelisted to interact with. + * + * @return A set of whitelisted sprinkler IDs. + */ Set whitelistSprinklers(); + /** + * Gets the lore (description text) of the watering can. + * + * @return A list of {@link TextValue} objects representing the lore. + */ List> lore(); + /** + * Gets the water bar that manages the watering can's water level. + * + * @return The {@link WaterBar} instance. + */ WaterBar waterBar(); + /** + * Gets the requirements for using the watering can. + * + * @return An array of {@link Requirement} instances for usage. + */ Requirement[] requirements(); + /** + * Checks if the watering can has infinite water capacity. + * + * @return true if the watering can is infinite, false otherwise. + */ boolean infinite(); + /** + * Gets the appearance of the watering can based on its water level. + * + * @param water The current water level. + * @return The appearance value corresponding to the water level. + */ Integer appearance(int water); + /** + * Gets the actions performed when the watering can is full. + * + * @return An array of full {@link Action}s. + */ Action[] fullActions(); + /** + * Gets the actions performed when water is added to the watering can. + * + * @return An array of add water {@link Action}s. + */ Action[] addWaterActions(); + /** + * Gets the actions performed when water is consumed from the watering can. + * + * @return An array of consume water {@link Action}s. + */ Action[] consumeWaterActions(); + /** + * Gets the actions performed when the watering can runs out of water. + * + * @return An array of run out of water {@link Action}s. + */ Action[] runOutOfWaterActions(); + /** + * Gets the actions performed when the watering can is used on a wrong pot. + * + * @return An array of wrong pot {@link Action}s. + */ Action[] wrongPotActions(); + /** + * Gets the actions performed when the watering can is used on a wrong sprinkler. + * + * @return An array of wrong sprinkler {@link Action}s. + */ Action[] wrongSprinklerActions(); + /** + * Gets the methods available for filling the watering can with water. + * + * @return An array of {@link FillMethod}s. + */ FillMethod[] fillMethods(); + /** + * Creates a new builder instance for constructing a {@link WateringCanConfig}. + * + * @return A new {@link Builder} instance. + */ static Builder builder() { return new WateringCanConfigImpl.BuilderImpl(); } + /** + * Builder interface for constructing instances of {@link WateringCanConfig} + */ interface Builder { - + /** + * Builds a new {@link WateringCanConfig} instance with the specified settings. + * + * @return A new {@link WateringCanConfig} instance. + */ WateringCanConfig build(); + /** + * Sets the unique identifier for the watering can configuration. + * + * @param id The unique ID for the watering can. + * @return The current instance of the Builder. + */ Builder id(String id); + /** + * Sets the item ID representing the watering can. + * + * @param itemID The item ID. + * @return The current instance of the Builder. + */ Builder itemID(String itemID); + /** + * Sets the width of the watering area for the watering can. + * + * @param width The width of the watering area. + * @return The current instance of the Builder. + */ Builder width(int width); + /** + * Sets the length of the watering area for the watering can. + * + * @param length The length of the watering area. + * @return The current instance of the Builder. + */ Builder length(int length); + /** + * Sets the maximum water storage capacity of the watering can. + * + * @param storage The maximum amount of water the watering can can store. + * @return The current instance of the Builder. + */ Builder storage(int storage); + /** + * Sets the amount of water added to pots per watering action. + * + * @param wateringAmount The amount of water consumed per use. + * @return The current instance of the Builder. + */ Builder wateringAmount(int wateringAmount); + /** + * Sets whether the lore (description text) of the watering can is dynamic. + * + * @param dynamicLore True if the lore is dynamic, false otherwise. + * @return The current instance of the Builder. + */ Builder dynamicLore(boolean dynamicLore); + /** + * Sets the whitelist of pot IDs that the watering can can interact with. + * + * @param whitelistPots A set of pot IDs that the watering can is allowed to interact with. + * @return The current instance of the Builder. + */ Builder potWhitelist(Set whitelistPots); + /** + * Sets the whitelist of sprinkler IDs that the watering can can interact with. + * + * @param whitelistSprinklers A set of sprinkler IDs that the watering can is allowed to interact with. + * @return The current instance of the Builder. + */ Builder sprinklerWhitelist(Set whitelistSprinklers); + /** + * Sets the lore (description text) for the watering can. + * + * @param lore A list of {@link TextValue} objects representing the lore text. + * @return The current instance of the Builder. + */ Builder lore(List> lore); + /** + * Sets the water bar that indicates the watering can's current water level. + * + * @param waterBar The {@link WaterBar} instance. + * @return The current instance of the Builder. + */ Builder waterBar(WaterBar waterBar); + /** + * Sets the requirements that must be met to use the watering can. + * + * @param requirements An array of {@link Requirement} instances. + * @return The current instance of the Builder. + */ Builder requirements(Requirement[] requirements); + /** + * Sets whether the watering can has infinite water capacity. + * + * @param infinite True if the watering can has infinite capacity, false otherwise. + * @return The current instance of the Builder. + */ Builder infinite(boolean infinite); + /** + * Sets the appearances of the watering can at different water levels. + * + * @param appearances A map where keys are water levels and values are appearance custom model IDs. + * @return The current instance of the Builder. + */ Builder appearances(Map appearances); + /** + * Sets the actions that are triggered when the watering can is full of water. + * + * @param fullActions An array of {@link Action} instances to be performed when the can is full. + * @return The current instance of the Builder. + */ Builder fullActions(Action[] fullActions); + /** + * Sets the actions that are triggered when water is added to the watering can. + * + * @param addWaterActions An array of {@link Action} instances to be performed when water is added. + * @return The current instance of the Builder. + */ Builder addWaterActions(Action[] addWaterActions); + /** + * Sets the actions that are triggered when water is consumed from the watering can. + * + * @param consumeWaterActions An array of {@link Action} instances to be performed when water is consumed. + * @return The current instance of the Builder. + */ Builder consumeWaterActions(Action[] consumeWaterActions); + /** + * Sets the actions that are triggered when the watering can runs out of water. + * + * @param runOutOfWaterActions An array of {@link Action} instances to be performed when the can is empty. + * @return The current instance of the Builder. + */ Builder runOutOfWaterActions(Action[] runOutOfWaterActions); + /** + * Sets the actions that are triggered when the watering can is used on the wrong pot. + * + * @param wrongPotActions An array of {@link Action} instances to be performed on wrong pot usage. + * @return The current instance of the Builder. + */ Builder wrongPotActions(Action[] wrongPotActions); + /** + * Sets the actions that are triggered when the watering can is used on the wrong sprinkler. + * + * @param wrongSprinklerActions An array of {@link Action} instances to be performed on wrong sprinkler usage. + * @return The current instance of the Builder. + */ Builder wrongSprinklerActions(Action[] wrongSprinklerActions); + /** + * Sets the methods available for filling the watering can with water. + * + * @param fillMethods An array of {@link FillMethod} instances. + * @return The current instance of the Builder. + */ Builder fillMethods(FillMethod[] fillMethods); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java index d91a1e106..f68c5464b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/BlockPos.java @@ -17,50 +17,110 @@ package net.momirealms.customcrops.api.core.world; +/** + * Represents a position within a chunk in the world. The position is encoded into a single integer for compact storage. + * The position is constructed using the x, y, and z coordinates, with the x and z limited to the range [0, 15] + * (as they represent positions within a 16x16 chunk), and y representing the height. + */ public class BlockPos { private final int position; + /** + * Constructs a BlockPos with an already encoded position. + * + * @param position The encoded position. + */ public BlockPos(int position) { this.position = position; } + /** + * Constructs a BlockPos from x, y, and z coordinates within a chunk. + * + * @param x The x-coordinate within the chunk (0-15). + * @param y The y-coordinate (world height). + * @param z The z-coordinate within the chunk (0-15). + */ public BlockPos(int x, int y, int z) { this.position = ((x & 0xF) << 28) | ((z & 0xF) << 24) | (y & 0xFFFFFF); } + /** + * Creates a BlockPos from a Pos3 object, adjusting x and z to be within the chunk. + * + * @param location The Pos3 object representing a 3D coordinate. + * @return A new BlockPos object. + */ public static BlockPos fromPos3(Pos3 location) { return new BlockPos(location.x() % 16, location.y(), location.z() % 16); } + /** + * Converts this BlockPos into a Pos3 object, calculating absolute world coordinates using the provided ChunkPos. + * + * @param coordinate The ChunkPos representing the chunk coordinates. + * @return A Pos3 object representing the world coordinates. + */ public Pos3 toPos3(ChunkPos coordinate) { return new Pos3(coordinate.x() * 16 + x(), y(), coordinate.z() * 16 + z()); } + /** + * Retrieves the encoded position value. + * + * @return The encoded position. + */ public int position() { return position; } + /** + * Retrieves the x-coordinate within the chunk. + * + * @return The x-coordinate (0-15). + */ public int x() { return (position >> 28) & 0xF; } + /** + * Retrieves the z-coordinate within the chunk. + * + * @return The z-coordinate (0-15). + */ public int z() { return (position >> 24) & 0xF; } + /** + * Retrieves the y-coordinate (world height). + * + * @return The y-coordinate. + */ public int y() { int y = position & 0xFFFFFF; - if ((y & 0x800000) != 0) { - y |= 0xFF000000; + if ((y & 0x800000) != 0) { // Check if y is negative in 24-bit signed integer representation + y |= 0xFF000000; // Extend the sign bit to make it a 32-bit signed integer. } return y; } + /** + * Calculates the section ID based on the y-coordinate. + * + * @return The section ID, representing which 16-block high section of the world the position is in. + */ public int sectionID() { return (int) Math.floor((double) y() / 16); } + /** + * Checks if this BlockPos is equal to another object. + * + * @param o The object to compare. + * @return true if the object is a BlockPos with the same encoded position, false otherwise. + */ @Override public boolean equals(Object o) { if (this == o) return true; @@ -69,11 +129,21 @@ public boolean equals(Object o) { return position == blockPos.position; } + /** + * Computes a hash code for this BlockPos. + * + * @return A hash code derived from the encoded position. + */ @Override public int hashCode() { return Math.abs(position); } + /** + * Returns a string representation of this BlockPos. + * + * @return A string in the format "BlockPos{x=..., y=..., z=...}". + */ @Override public String toString() { return "BlockPos{" + diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java index 044f0171f..d80d081a7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/ChunkPos.java @@ -20,12 +20,29 @@ import org.bukkit.Chunk; import org.jetbrains.annotations.NotNull; +/** + * Represents the position of a chunk in a Minecraft world using chunk coordinates (x, z). + */ public record ChunkPos(int x, int z) { + /** + * Creates a new ChunkPos instance with the specified chunk coordinates. + * + * @param x The x-coordinate of the chunk. + * @param z The z-coordinate of the chunk. + * @return A new ChunkPos instance. + */ public static ChunkPos of(int x, int z) { return new ChunkPos(x, z); } + /** + * Parses a ChunkPos from a string representation in the format "x,z". + * + * @param coordinate The string representation of the chunk coordinates. + * @return A new ChunkPos instance parsed from the string. + * @throws RuntimeException If the string cannot be parsed into valid coordinates. + */ public static ChunkPos fromString(String coordinate) { String[] split = coordinate.split(",", 2); try { @@ -37,18 +54,35 @@ public static ChunkPos fromString(String coordinate) { } } + /** + * Creates a ChunkPos from a Pos3 object by calculating which chunk the position falls into. + * + * @param pos3 The Pos3 object representing a 3D position. + * @return A ChunkPos representing the chunk containing the provided position. + */ public static ChunkPos fromPos3(Pos3 pos3) { int chunkX = (int) Math.floor((double) pos3.x() / 16.0); int chunkZ = (int) Math.floor((double) pos3.z() / 16.0); return ChunkPos.of(chunkX, chunkZ); } + /** + * Computes a hash code for this ChunkPos. + * + * @return A hash code representing this ChunkPos. + */ @Override public int hashCode() { long combined = (long) x << 32 | z; return Long.hashCode(combined); } + /** + * Checks if this ChunkPos is equal to another object. + * + * @param obj The object to compare. + * @return true if the object is a ChunkPos with the same x and z coordinates, false otherwise. + */ @Override public boolean equals(Object obj) { if (obj == null) { @@ -67,20 +101,41 @@ public boolean equals(Object obj) { return true; } + /** + * Converts this ChunkPos to a RegionPos, which represents a larger area containing multiple chunks. + * + * @return A RegionPos representing the region containing this chunk. + */ @NotNull public RegionPos toRegionPos() { return RegionPos.getByChunkPos(this); } + /** + * Creates a ChunkPos from a Bukkit Chunk object. + * + * @param chunk The Bukkit Chunk object. + * @return A ChunkPos representing the chunk's coordinates. + */ @NotNull public static ChunkPos fromBukkitChunk(@NotNull Chunk chunk) { return new ChunkPos(chunk.getX(), chunk.getZ()); } + /** + * Converts this ChunkPos to a string representation in the format "x,z". + * + * @return A string representing this ChunkPos. + */ public String asString() { return x + "," + z; } + /** + * Returns a string representation of this ChunkPos for debugging and logging purposes. + * + * @return A string in the format "ChunkPos{x=..., z=...}". + */ @Override public String toString() { return "ChunkPos{" + @@ -88,4 +143,4 @@ public String toString() { ", z=" + z + '}'; } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java index acd2c11aa..4b6b44bf3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsBlockState.java @@ -21,11 +21,26 @@ import net.momirealms.customcrops.api.core.block.CustomCropsBlock; import org.jetbrains.annotations.NotNull; +/** + * Interface representing the state of a custom crops block in the CustomCrops plugin. + */ public interface CustomCropsBlockState extends DataBlock { + /** + * Retrieves the type of the custom crops block associated with this state. + * + * @return The {@link CustomCropsBlock} type of this block state. + */ @NotNull CustomCropsBlock type(); + /** + * Creates a new instance of {@link CustomCropsBlockState} with the given block type and NBT data. + * + * @param owner The custom crops block type that owns this state. + * @param compoundMap The NBT data associated with this block state. + * @return A new instance of {@link CustomCropsBlockState} representing the specified block type and state. + */ static CustomCropsBlockState create(CustomCropsBlock owner, CompoundMap compoundMap) { return new CustomCropsBlockStateImpl(owner, compoundMap); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java index 3adc0b01a..a84c7f4b4 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java @@ -24,171 +24,215 @@ import java.util.Set; import java.util.stream.Stream; +/** + * Interface representing a chunk in the CustomCrops plugin + */ public interface CustomCropsChunk { /** - * Set if the chunk can be force loaded. - * If a chunk is force loaded, no one can unload it unless you set force load to false. - * This can prevent CustomCrops from unloading the chunks on {@link org.bukkit.event.world.ChunkUnloadEvent} + * Sets whether the chunk can be force loaded. + * If a chunk is force loaded, it cannot be unloaded unless force loading is set to false. + * This prevents CustomCrops from unloading the chunk during a {@link org.bukkit.event.world.ChunkUnloadEvent}. * - * This value will not be persistently stored. Please use {@link org.bukkit.World#setChunkForceLoaded(int, int, boolean)} - * if you want to force a chunk loaded. + * Note: This value is not persistently stored. To force a chunk to stay loaded persistently, + * use {@link org.bukkit.World#setChunkForceLoaded(int, int, boolean)}. * - * @param forceLoad force loaded + * @param forceLoad Whether the chunk should be force loaded. */ void setForceLoaded(boolean forceLoad); /** - * Indicates whether the chunk is force loaded + * Checks if the chunk is force loaded. * - * @return force loaded or not + * @return true if the chunk is force loaded, false otherwise. */ boolean isForceLoaded(); /** - * Loads the chunk to cache and participate in the mechanism of the plugin. + * Loads the chunk into the cache to participate in the plugin's mechanisms. * - * @param loadBukkitChunk whether to load Bukkit chunks temporarily if it's not loaded + * @param loadBukkitChunk Whether to temporarily load the Bukkit chunk if it is not already loaded. */ void load(boolean loadBukkitChunk); /** - * Unloads the chunk. Lazy refer to those chunks that will be delayed for unloading. - * Recently unloaded chunks are likely to be loaded again soon. + * Unloads the chunk, with an option for a lazy unload. + * Lazy unloading delays the unload, which is useful if the chunk is likely to be loaded again soon. * - * @param lazy delay unload or not + * @param lazy Whether to delay the unload (lazy unload). */ void unload(boolean lazy); /** - * Unloads the chunk if it is a lazy chunk + * Unloads the chunk if it is marked as lazy. */ void unloadLazy(); /** - * Indicates whether the chunk is in lazy state + * Checks if the chunk is marked as lazy. * - * @return lazy or not + * @return true if the chunk is lazy, false otherwise. */ boolean isLazy(); /** - * Indicates whether the chunk is loaded + * Checks if the chunk is currently loaded. * - * @return loaded or not + * @return true if the chunk is loaded, false otherwise. */ boolean isLoaded(); /** - * Get the world associated with the chunk + * Gets the world associated with this chunk. * - * @return CustomCrops world + * @return The {@link CustomCropsWorld} instance. */ CustomCropsWorld getWorld(); /** - * Get the position of the chunk + * Gets the position of this chunk. * - * @return chunk position + * @return The {@link ChunkPos} representing the chunk's position. */ ChunkPos chunkPos(); /** - * Do second timer + * Executes a timer task associated with this chunk. */ void timer(); /** - * Get the unloaded time in seconds - * This value would increase if the chunk is lazy + * Gets the time in seconds since the chunk was unloaded. + * This value increases if the chunk is in a lazy state. * - * @return the unloaded time + * @return The unloaded time in seconds. */ int unloadedSeconds(); /** - * Set the unloaded seconds + * Sets the time in seconds since the chunk was unloaded. * - * @param unloadedSeconds unloadedSeconds + * @param unloadedSeconds The unloaded time to set. */ void unloadedSeconds(int unloadedSeconds); /** - * Get the last loaded time + * Gets the last time the chunk was loaded. * - * @return last loaded time + * @return The timestamp of the last loaded time. */ long lastLoadedTime(); /** - * Set the last loaded time to current time + * Updates the last loaded time to the current time. */ void updateLastLoadedTime(); /** - * Get the loaded time in seconds + * Gets the time in milliseconds since the chunk was loaded. * - * @return loaded time + * @return The loaded time in milliseconds. */ int loadedMilliSeconds(); /** - * Get block data at a certain location + * Retrieves the custom crop block state at a specific location. * - * @param location location - * @return block data + * @param location The location to check. + * @return An {@link Optional} containing the {@link CustomCropsBlockState} if present, otherwise empty. */ @NotNull Optional getBlockState(Pos3 location); /** - * Remove any block data from a certain location + * Removes any custom crop block state at a specific location. * - * @param location location - * @return block data + * @param location The location from which to remove the block state. + * @return An {@link Optional} containing the removed {@link CustomCropsBlockState} if present, otherwise empty. */ @NotNull Optional removeBlockState(Pos3 location); /** - * Add a custom block data at a certain location + * Adds a custom crop block state at a specific location. * - * @param block block to add - * @return the previous block data + * @param location The location to add the block state. + * @param block The custom crop block state to add. + * @return An {@link Optional} containing the previous {@link CustomCropsBlockState} if replaced, otherwise empty. */ @NotNull Optional addBlockState(Pos3 location, CustomCropsBlockState block); /** - * Get CustomCrops sections + * Gets a stream of custom crop sections that need to be saved. * - * @return sections + * @return A {@link Stream} of {@link CustomCropsSection} to save. */ @NotNull Stream sectionsToSave(); /** - * Get section by ID + * Retrieves a loaded section by its ID. * - * @param sectionID id - * @return section + * @param sectionID The ID of the section to retrieve. + * @return An {@link Optional} containing the {@link CustomCropsSection} if loaded, otherwise empty. */ @NotNull Optional getLoadedSection(int sectionID); + /** + * Retrieves a section by its ID, loading it if necessary. + * + * @param sectionID The ID of the section to retrieve. + * @return The {@link CustomCropsSection} instance. + */ CustomCropsSection getSection(int sectionID); + /** + * Retrieves all sections within this chunk. + * + * @return An array of {@link CustomCropsSection}. + */ CustomCropsSection[] sections(); + /** + * Removes a section by its ID. + * + * @param sectionID The ID of the section to remove. + * @return An {@link Optional} containing the removed {@link CustomCropsSection} if present, otherwise empty. + */ Optional removeSection(int sectionID); + /** + * Resets the unloaded time counter to zero. + */ void resetUnloadedSeconds(); + /** + * Checks if the chunk can be pruned (removed from memory or storage). + * + * @return true if the chunk can be pruned, false otherwise. + */ boolean canPrune(); + /** + * Checks if offline tasks have been notified for this chunk. + * + * @return true if offline tasks are notified, false otherwise. + */ boolean isOfflineTaskNotified(); + /** + * Gets the queue of delayed tick tasks for this chunk. + * + * @return A {@link PriorityQueue} of {@link DelayedTickTask}. + */ PriorityQueue tickTaskQueue(); + /** + * Gets the set of blocks that have been ticked in one tick cycle within this chunk. + * + * @return A {@link Set} of {@link BlockPos} representing ticked blocks. + */ Set tickedBlocks(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java index 1746bcf12..ce9a9d402 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegion.java @@ -21,26 +21,79 @@ import java.util.Map; +/** + * Interface representing a region in the CustomCrops plugin + */ public interface CustomCropsRegion { + /** + * Checks if the region is currently loaded. + * + * @return true if the region is loaded, false otherwise. + */ boolean isLoaded(); + /** + * Unloads the region, freeing up resources. + */ void unload(); + /** + * Loads the region into memory, preparing it for operations. + */ void load(); + /** + * Gets the world associated with this region. + * + * @return The {@link CustomCropsWorld} instance representing the world. + */ @NotNull CustomCropsWorld getWorld(); + /** + * Retrieves the cached data of a chunk within this region by its position. + * + * @param pos The {@link ChunkPos} representing the position of the chunk. + * @return A byte array representing the cached data of the chunk, or null if no data is cached. + */ byte[] getCachedChunkBytes(ChunkPos pos); + /** + * Gets the position of this region. + * + * @return The {@link RegionPos} representing the region's position. + */ + @NotNull RegionPos regionPos(); + /** + * Removes the cached data of a chunk from this region by its position. + * + * @param pos The {@link ChunkPos} representing the position of the chunk. + * @return true if the cached data was removed successfully, false otherwise. + */ boolean removeCachedChunk(ChunkPos pos); + /** + * Caches the data of a chunk within this region at the specified position. + * + * @param pos The {@link ChunkPos} representing the position of the chunk. + * @param data A byte array representing the data to cache for the chunk. + */ void setCachedChunk(ChunkPos pos, byte[] data); + /** + * Retrieves a map of all chunks and their data that need to be saved. + * + * @return A {@link Map} where the key is {@link ChunkPos} and the value is a byte array of the chunk data. + */ Map dataToSave(); + /** + * Checks if the region can be pruned (removed from memory or storage). + * + * @return true if the region can be pruned, false otherwise. + */ boolean canPrune(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java index 04afd671b..5bfed3a7f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsRegionImpl.java @@ -76,6 +76,7 @@ public byte[] getCachedChunkBytes(ChunkPos pos) { return this.cachedChunks.get(pos); } + @NotNull @Override public RegionPos regionPos() { return this.regionPos; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java index 0a6b22e58..0394d5b84 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsSection.java @@ -23,30 +23,85 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +/** + * Interface representing a section of a chunk in the CustomCrops plugin + */ public interface CustomCropsSection { + /** + * Creates a new instance of a CustomCropsSection with the specified section ID. + * + * @param sectionID The ID of the section to create. + * @return A new {@link CustomCropsSection} instance. + */ static CustomCropsSection create(int sectionID) { return new CustomCropsSectionImpl(sectionID); } + /** + * Restores an existing CustomCropsSection from the provided section ID and block states. + * + * @param sectionID The ID of the section to restore. + * @param blocks A map of {@link BlockPos} to {@link CustomCropsBlockState} representing the blocks in the section. + * @return A restored {@link CustomCropsSection} instance. + */ static CustomCropsSection restore(int sectionID, ConcurrentHashMap blocks) { return new CustomCropsSectionImpl(sectionID, blocks); } + /** + * Gets the ID of this section. + * + * @return The section ID. + */ int getSectionID(); + /** + * Retrieves the block state at a specific position within this section. + * + * @param pos The {@link BlockPos} representing the position of the block. + * @return An {@link Optional} containing the {@link CustomCropsBlockState} if present, otherwise empty. + */ @NotNull Optional getBlockState(BlockPos pos); + /** + * Removes the block state at a specific position within this section. + * + * @param pos The {@link BlockPos} representing the position of the block to remove. + * @return An {@link Optional} containing the removed {@link CustomCropsBlockState} if present, otherwise empty. + */ @NotNull Optional removeBlockState(BlockPos pos); + /** + * Adds or replaces a block state at a specific position within this section. + * + * @param pos The {@link BlockPos} representing the position where the block will be added. + * @param block The {@link CustomCropsBlockState} to add. + * @return An {@link Optional} containing the previous {@link CustomCropsBlockState} if replaced, otherwise empty. + */ @NotNull Optional addBlockState(BlockPos pos, CustomCropsBlockState block); + /** + * Checks if the section can be pruned (removed from memory or storage). + * + * @return true if the section can be pruned, false otherwise. + */ boolean canPrune(); + /** + * Gets an array of all block states within this section. + * + * @return An array of {@link CustomCropsBlockState}. + */ CustomCropsBlockState[] blocks(); + /** + * Gets a map of all block positions to their respective block states within this section. + * + * @return A {@link Map} of {@link BlockPos} to {@link CustomCropsBlockState}. + */ Map blockMap(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java index ebb579371..18bd9806e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java @@ -27,30 +27,46 @@ import java.util.PriorityQueue; import java.util.concurrent.ConcurrentHashMap; +/** + * Interface representing a custom world in the CustomCrops plugin + * + * @param The type representing the world (e.g., Bukkit World). + */ public interface CustomCropsWorld { /** - * Create a CustomCrops world + * Creates a new CustomCrops world with the specified world instance and adaptor. * - * @param world world instance - * @param adaptor the world adaptor - * @return CustomCrops world - * @param world type + * @param world The world instance. + * @param adaptor The world adaptor to use for this world. + * @param The type of the world. + * @return A new instance of {@link CustomCropsWorld}. */ static CustomCropsWorld create(W world, WorldAdaptor adaptor) { return new CustomCropsWorldImpl<>(world, adaptor); } /** - * Create a new CustomCropsChunk associated with this world + * Creates a new CustomCropsChunk associated with this world at the specified position. * - * @param pos the position of the chunk - * @return the created chunk + * @param pos The position of the chunk. + * @return The created {@link CustomCropsChunk}. */ default CustomCropsChunk createChunk(ChunkPos pos) { return new CustomCropsChunkImpl(this, pos); } + /** + * Restores a CustomCropsChunk with the specified parameters. + * + * @param pos The position of the chunk. + * @param loadedSeconds The number of seconds the chunk has been loaded. + * @param lastLoadedTime The last time the chunk was loaded. + * @param loadedSections The sections loaded in this chunk. + * @param queue The queue of delayed tick tasks. + * @param tickedBlocks The set of blocks that have been ticked. + * @return The restored {@link CustomCropsChunk}. + */ default CustomCropsChunk restoreChunk( ChunkPos pos, int loadedSeconds, @@ -62,156 +78,221 @@ default CustomCropsChunk restoreChunk( return new CustomCropsChunkImpl(this, pos, loadedSeconds, lastLoadedTime, loadedSections, queue, tickedBlocks); } + /** + * Creates a new CustomCropsRegion associated with this world at the specified position. + * + * @param pos The position of the region. + * @return The created {@link CustomCropsRegion}. + */ default CustomCropsRegion createRegion(RegionPos pos) { return new CustomCropsRegionImpl(this, pos); } + /** + * Restores a CustomCropsRegion with the specified cached chunks. + * + * @param pos The position of the region. + * @param cachedChunks The map of cached chunks within the region. + * @return The restored {@link CustomCropsRegion}. + */ default CustomCropsRegion restoreRegion(RegionPos pos, ConcurrentHashMap cachedChunks) { return new CustomCropsRegionImpl(this, pos, cachedChunks); } + /** + * Gets the world adaptor associated with this world. + * + * @return The {@link WorldAdaptor} for this world. + */ WorldAdaptor adaptor(); + /** + * Gets the extra data associated with this world. + * + * @return The {@link WorldExtraData} instance. + */ WorldExtraData extraData(); + /** + * Tests if adding a specified amount of blocks of a certain type would exceed + * the chunk limitation for that block type. + * + * @param pos3 The position to test. + * @param clazz The class of the block type. + * @param amount The number of blocks to add. + * @return true if it would exceed the limit, false otherwise. + */ boolean testChunkLimitation(Pos3 pos3, Class clazz, int amount); + /** + * Checks if a chunk contains any blocks of a specific type. + * + * @param pos3 The position to check. + * @param clazz The class of the block type. + * @return true if the chunk contains the block type, false otherwise. + */ boolean doesChunkHaveBlock(Pos3 pos3, Class clazz); + /** + * Gets the number of blocks of a specific type in a chunk. + * + * @param pos3 The position to check. + * @param clazz The class of the block type. + * @return The number of blocks of the specified type in the chunk. + */ int getChunkBlockAmount(Pos3 pos3, Class clazz); + /** + * Gets all the loaded chunks in this world. + * + * @return An array of {@link CustomCropsChunk} representing the loaded chunks. + */ CustomCropsChunk[] loadedChunks(); /** - * Get the state of the block at a certain location + * Gets the block state at a specific location. * - * @param location location of the block state - * @return the optional block state + * @param location The location of the block state. + * @return An {@link Optional} containing the block state if present, otherwise empty. */ @NotNull Optional getBlockState(Pos3 location); /** - * Remove the block state from a certain location + * Removes the block state at a specific location. * - * @param location the location of the block state - * @return the optional removed state + * @param location The location of the block state to remove. + * @return An {@link Optional} containing the removed block state if present, otherwise empty. */ @NotNull Optional removeBlockState(Pos3 location); /** - * Add block state at the certain location + * Adds a block state at a specific location. * - * @param location location of the state - * @param block block state to add - * @return the optional previous state + * @param location The location of the block state. + * @param block The block state to add. + * @return An {@link Optional} containing the previous block state if replaced, otherwise empty. */ @NotNull Optional addBlockState(Pos3 location, CustomCropsBlockState block); /** - * Save the world to file + * Saves the world data to a file. */ void save(); /** - * Set if the ticking task is ongoing + * Sets whether the ticking task is ongoing. * - * @param tick ongoing or not + * @param tick true if ticking is ongoing, false otherwise. */ void setTicking(boolean tick); /** - * Get the world associated with this world + * Gets the underlying world instance associated with this CustomCrops world. * - * @return Bukkit world + * @return The world instance of type W. */ W world(); + /** + * Gets the Bukkit World instance associated with this CustomCrops world. + * + * @return The Bukkit {@link World} instance. + */ World bukkitWorld(); + /** + * Gets the name of the world. + * + * @return The world name. + */ String worldName(); /** - * Get the settings of the world + * Gets the settings associated with this world. * - * @return the setting + * @return The {@link WorldSetting} instance. */ @NotNull WorldSetting setting(); /** - * Set the settings of the world + * Sets the settings for this world. * - * @param setting setting + * @param setting The {@link WorldSetting} to apply. */ void setting(WorldSetting setting); - /* - * Chunks + /** + * Checks if a chunk is loaded in this world. + * + * @param pos The position of the chunk. + * @return true if the chunk is loaded, false otherwise. */ boolean isChunkLoaded(ChunkPos pos); /** - * Get loaded chunk from cache + * Gets a loaded chunk from the cache, if available. * - * @param chunkPos the position of the chunk - * @return the optional loaded chunk + * @param chunkPos The position of the chunk. + * @return An {@link Optional} containing the loaded chunk if present, otherwise empty. */ @NotNull Optional getLoadedChunk(ChunkPos chunkPos); /** - * Get chunk from cache or file + * Gets a chunk from the cache or loads it from file if not cached. * - * @param chunkPos the position of the chunk - * @return the optional chunk + * @param chunkPos The position of the chunk. + * @return An {@link Optional} containing the chunk if present, otherwise empty. */ @NotNull Optional getChunk(ChunkPos chunkPos); /** - * Get chunk from cache or file, create if not found + * Gets a chunk from the cache or loads it from file, creating a new one if it does not exist. * - * @param chunkPos the position of the chunk - * @return the chunk + * @param chunkPos The position of the chunk. + * @return The {@link CustomCropsChunk}. */ @NotNull CustomCropsChunk getOrCreateChunk(ChunkPos chunkPos); /** - * Check if a region is loaded + * Checks if a region is loaded in this world. * - * @param regionPos the position of the region - * @return loaded or not + * @param regionPos The position of the region. + * @return true if the region is loaded, false otherwise. */ boolean isRegionLoaded(RegionPos regionPos); /** - * Get the loaded region, empty if not loaded + * Gets a loaded region from the cache, if available. * - * @param regionPos position of the region - * @return the optional loaded region + * @param regionPos The position of the region. + * @return An {@link Optional} containing the loaded region if present, otherwise empty. */ @NotNull Optional getLoadedRegion(RegionPos regionPos); /** - * Get the region from cache or file + * Gets a region from the cache or loads it from file if not cached. * - * @param regionPos position of the region - * @return the optional region + * @param regionPos The position of the region. + * @return An {@link Optional} containing the region if present, otherwise empty. */ @NotNull Optional getRegion(RegionPos regionPos); /** - * Get the region from cache or file, create if not found + * Gets a region from the cache or loads it from file, creating a new one if it does not exist. * - * @param regionPos position of the region - * @return the region + * @param regionPos The position of the region. + * @return The {@link CustomCropsRegion}. */ @NotNull CustomCropsRegion getOrCreateRegion(RegionPos regionPos); } + diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java index 94c3a3153..ca65d0e23 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java @@ -60,9 +60,10 @@ public CustomCropsWorldImpl(W world, WorldAdaptor adaptor) { this.regionTimer = 0; this.adaptor = adaptor; this.extraData = adaptor.loadExtraData(world); - this.currentMinecraftDay = (int) (adaptor.getWorldFullTime(world) / 24_000); + this.currentMinecraftDay = (int) (bukkitWorld().getFullTime() / 24_000); } + @NotNull @Override public WorldAdaptor adaptor() { return adaptor; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java index 227a7cecd..57e5a173e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/DataBlock.java @@ -20,18 +20,40 @@ import com.flowpowered.nbt.Tag; import net.momirealms.customcrops.api.core.SynchronizedCompoundMap; +/** + * Interface representing a data block that can store, retrieve, and manipulate NBT data. + */ public interface DataBlock { + /** + * Sets an NBT tag in the data block with the specified key. + * + * @param key The key for the tag to set. + * @param tag The NBT tag to set. + * @return The previous tag associated with the key, or null if there was no previous tag. + */ Tag set(String key, Tag tag); + /** + * Retrieves an NBT tag from the data block with the specified key. + * + * @param key The key of the tag to retrieve. + * @return The NBT tag associated with the key, or null if no tag is found. + */ Tag get(String key); + /** + * Removes an NBT tag from the data block with the specified key. + * + * @param key The key of the tag to remove. + * @return The removed NBT tag, or null if no tag was found with the specified key. + */ Tag remove(String key); /** - * Get the data map + * Gets the synchronized compound map containing all the NBT data of the block. * - * @return data map + * @return The {@link SynchronizedCompoundMap} containing the block's NBT data. */ SynchronizedCompoundMap compoundMap(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/Pos3.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/Pos3.java index 4be100f3b..05a2a4eb8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/Pos3.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/Pos3.java @@ -20,8 +20,17 @@ import org.bukkit.Location; import org.bukkit.World; +/** + * Represents a 3-dimensional position (x, y, z) in a Minecraft world. + */ public record Pos3(int x, int y, int z) { + /** + * Checks if this position is equal to another object. + * + * @param obj The object to compare with. + * @return true if the object is a Pos3 with the same coordinates, false otherwise. + */ @Override public boolean equals(Object obj) { if (obj == null) { @@ -43,6 +52,11 @@ public boolean equals(Object obj) { return true; } + /** + * Computes a hash code for this position. + * + * @return A hash code representing this Pos3. + */ @Override public int hashCode() { int hash = 3; @@ -52,30 +66,70 @@ public int hashCode() { return hash; } + /** + * Converts a Bukkit {@link Location} to a Pos3 instance. + * + * @param location The Bukkit location to convert. + * @return A new Pos3 instance representing the block coordinates of the location. + */ public static Pos3 from(Location location) { return new Pos3(location.getBlockX(), location.getBlockY(), location.getBlockZ()); } + /** + * Converts this Pos3 instance to a Bukkit {@link Location}. + * + * @param world The Bukkit world to associate with the location. + * @return A new Location instance with this Pos3's coordinates in the specified world. + */ public Location toLocation(World world) { return new Location(world, x, y, z); } + /** + * Adds the specified values to this position's coordinates and returns a new Pos3 instance. + * + * @param x The amount to add to the x-coordinate. + * @param y The amount to add to the y-coordinate. + * @param z The amount to add to the z-coordinate. + * @return A new Pos3 instance with updated coordinates. + */ public Pos3 add(int x, int y, int z) { return new Pos3(this.x + x, this.y + y, this.z + z); } + /** + * Converts this Pos3 instance to a {@link ChunkPos}, representing the chunk coordinates of this position. + * + * @return The {@link ChunkPos} containing this Pos3. + */ public ChunkPos toChunkPos() { return ChunkPos.fromPos3(this); } + /** + * Calculates the chunk x-coordinate of this position. + * + * @return The chunk x-coordinate. + */ public int chunkX() { return (int) Math.floor((double) this.x() / 16.0); } + /** + * Calculates the chunk z-coordinate of this position. + * + * @return The chunk z-coordinate. + */ public int chunkZ() { return (int) Math.floor((double) this.z() / 16.0); } + /** + * Returns a string representation of this Pos3 instance for debugging and logging. + * + * @return A string in the format "Pos3{x=..., y=..., z=...}". + */ @Override public String toString() { return "Pos3{" + @@ -84,4 +138,4 @@ public String toString() { ", z=" + z + '}'; } -} \ No newline at end of file +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java index e4d530cad..aab0dcc4b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/RegionPos.java @@ -20,12 +20,30 @@ import org.bukkit.Chunk; import org.jetbrains.annotations.NotNull; +/** + * Represents a region position in a Minecraft world, defined by its x and z coordinates. + */ public record RegionPos(int x, int z) { + /** + * Creates a new RegionPos instance with the specified x and z coordinates. + * + * @param x The x-coordinate of the region. + * @param z The z-coordinate of the region. + * @return A new {@link RegionPos} instance. + */ public static RegionPos of(int x, int z) { return new RegionPos(x, z); } + /** + * Parses a string representation of a region position and returns a RegionPos instance. + * The string should be in the format "x,z". + * + * @param coordinate The string representation of the region position. + * @return A new {@link RegionPos} instance. + * @throws RuntimeException if the coordinate string is invalid. + */ public static RegionPos getByString(String coordinate) { String[] split = coordinate.split(",", 2); try { @@ -38,12 +56,23 @@ public static RegionPos getByString(String coordinate) { } } + /** + * Computes a hash code for this region position. + * + * @return A hash code representing this {@link RegionPos}. + */ @Override public int hashCode() { long combined = (long) x << 32 | z; return Long.hashCode(combined); } + /** + * Checks if this region position is equal to another object. + * + * @param obj The object to compare with. + * @return true if the object is a {@link RegionPos} with the same coordinates, false otherwise. + */ @Override public boolean equals(Object obj) { if (obj == null) { @@ -62,6 +91,13 @@ public boolean equals(Object obj) { return true; } + /** + * Converts a Bukkit {@link Chunk} to a {@link RegionPos} by calculating the region coordinates. + * A region covers a 32x32 area of chunks. + * + * @param chunk The Bukkit chunk to convert. + * @return A new {@link RegionPos} representing the region containing the chunk. + */ @NotNull public static RegionPos getByBukkitChunk(@NotNull Chunk chunk) { int regionX = (int) Math.floor((double) chunk.getX() / 32.0); @@ -69,6 +105,13 @@ public static RegionPos getByBukkitChunk(@NotNull Chunk chunk) { return new RegionPos(regionX, regionZ); } + /** + * Converts a {@link ChunkPos} to a {@link RegionPos} by calculating the region coordinates. + * A region covers a 32x32 area of chunks. + * + * @param chunk The {@link ChunkPos} to convert. + * @return A new {@link RegionPos} representing the region containing the chunk position. + */ @NotNull public static RegionPos getByChunkPos(@NotNull ChunkPos chunk) { int regionX = (int) Math.floor((double) chunk.x() / 32.0); @@ -76,6 +119,11 @@ public static RegionPos getByChunkPos(@NotNull ChunkPos chunk) { return new RegionPos(regionX, regionZ); } + /** + * Returns a string representation of this RegionPos instance for debugging and logging. + * + * @return A string in the format "RegionPos{x=..., z=...}". + */ @Override public String toString() { return "RegionPos{" + @@ -84,3 +132,4 @@ public String toString() { '}'; } } + diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java index 650eafc77..95dd596ca 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldExtraData.java @@ -22,6 +22,12 @@ import java.util.HashMap; +/** + * A class representing additional data associated with a world, such as the current season, + * date, and any extra arbitrary data stored as key-value pairs. This class provides methods + * for managing the season and date of the world, as well as adding, removing, and retrieving + * extra data. + */ public class WorldExtraData { @SerializedName("season") @@ -31,33 +37,62 @@ public class WorldExtraData { @SerializedName("extra") private HashMap extra; + /** + * Constructs a new WorldExtraData instance with the specified season and date. + * Initializes an empty HashMap for extra data. + * + * @param season The initial season of the world. + * @param date The initial date of the world. + */ public WorldExtraData(Season season, int date) { this.season = season; this.date = date; this.extra = new HashMap<>(); } + /** + * Creates an empty WorldExtraData instance with default values. + * + * @return A new WorldExtraData instance with the season set to SPRING and date set to 1. + */ public static WorldExtraData empty() { return new WorldExtraData(Season.SPRING, 1); } + /** + * Adds extra data to the world data storage. + * + * @param key The key under which the data will be stored. + * @param value The value to store. + */ public void addExtraData(String key, Object value) { this.extra.put(key, value); } + /** + * Removes extra data from the world data storage. + * + * @param key The key of the data to remove. + */ public void removeExtraData(String key) { this.extra.remove(key); } + /** + * Retrieves extra data from the world data storage. + * + * @param key The key of the data to retrieve. + * @return The data associated with the key, or null if the key does not exist. + */ @Nullable public Object getExtraData(String key) { return this.extra.get(key); } /** - * Get season + * Gets the current season of the world. * - * @return season + * @return The current season. */ public Season getSeason() { if (season == null) season = Season.SPRING; @@ -65,37 +100,43 @@ public Season getSeason() { } /** - * Set season + * Sets the season of the world. * - * @param season the new season + * @param season The new season to set. */ public void setSeason(Season season) { this.season = season; } /** - * Get date + * Gets the current date of the world. * - * @return date + * @return The current date. */ public int getDate() { return date; } /** - * Set date + * Sets the date of the world. * - * @param date the new date + * @param date The new date to set. */ public void setDate(int date) { this.date = date; } + /** + * Returns a string representation of the WorldExtraData. + * + * @return A string containing the season and date information. + */ @Override public String toString() { return "WorldExtraData{" + "season=" + season + ", date=" + date + + ", extra=" + extra + '}'; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java index 17b9a33cd..8085cd9f7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java @@ -24,28 +24,98 @@ import java.util.Optional; import java.util.Set; +import java.util.TreeSet; +/** + * WorldManager is responsible for managing the lifecycle and state of worlds in the CustomCrops plugin. + * It provides methods to load, unload, and adapt worlds, manage seasonal changes, and interact with + * different world adaptors. + */ public interface WorldManager extends Reloadable { + /** + * Gets the current SeasonProvider used for determining the season of each world. + * + * @return The current SeasonProvider. + */ SeasonProvider seasonProvider(); + /** + * Retrieves the current season for a given Bukkit world. + * + * @param world The Bukkit world to get the season for. + * @return The current season of the specified world. + */ Season getSeason(World world); + /** + * Retrieves the current date for a given Bukkit world. + * + * @param world The Bukkit world to get the date for. + * @return The current date of the specified world. + */ int getDate(World world); + /** + * Loads a CustomCrops world based on the specified Bukkit world. + * + * @param world The Bukkit world to load as a CustomCrops world. + * @return The loaded CustomCropsWorld instance. + */ CustomCropsWorld loadWorld(World world); + /** + * Unloads the CustomCrops world associated with the specified Bukkit world. + * + * @param world The Bukkit world to unload. + * @return True if the world was successfully unloaded, false otherwise. + */ boolean unloadWorld(World world); + /** + * Retrieves a CustomCrops world based on the specified Bukkit world, if loaded. + * + * @param world The Bukkit world to retrieve the CustomCrops world for. + * @return An Optional containing the CustomCropsWorld instance if loaded, otherwise empty. + */ Optional> getWorld(World world); + /** + * Retrieves a CustomCrops world based on the world name, if loaded. + * + * @param world The name of the world to retrieve. + * @return An Optional containing the CustomCropsWorld instance if loaded, otherwise empty. + */ Optional> getWorld(String world); + /** + * Checks if a given Bukkit world is currently loaded as a CustomCrops world. + * + * @param world The Bukkit world to check. + * @return True if the world is loaded, false otherwise. + */ boolean isWorldLoaded(World world); - Set> adaptors(); + /** + * Retrieves all available world adaptors. + * + * @return A set of WorldAdaptor instances. + */ + TreeSet> adaptors(); + /** + * Adapts a Bukkit world into a CustomCrops world. + * + * @param world The Bukkit world to adapt. + * @return The adapted CustomCropsWorld instance. + */ CustomCropsWorld adapt(World world); + /** + * Adapts a world by its name into a CustomCrops world. + * + * @param world The name of the world to adapt. + * @return The adapted CustomCropsWorld instance. + */ CustomCropsWorld adapt(String world); -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java index 55737bbf9..cf87fd16a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java @@ -17,6 +17,10 @@ package net.momirealms.customcrops.api.core.world; +/** + * Represents the configuration settings for a CustomCrops world, including various + * parameters for ticking behavior, season management, and random events. + */ public class WorldSetting implements Cloneable { private final boolean enableScheduler; @@ -37,6 +41,27 @@ public class WorldSetting implements Cloneable { private final boolean tickPotRandomly; private final boolean tickSprinklerRandomly; + /** + * Private constructor to initialize a WorldSetting instance with the provided parameters. + * + * @param enableScheduler Whether the scheduler is enabled. + * @param minTickUnit The minimum unit of tick. + * @param tickCropRandomly Whether crops are ticked randomly. + * @param tickCropInterval The interval for ticking crops. + * @param tickPotRandomly Whether pots are ticked randomly. + * @param tickPotInterval The interval for ticking pots. + * @param tickSprinklerRandomly Whether sprinklers are ticked randomly. + * @param tickSprinklerInterval The interval for ticking sprinklers. + * @param offlineTick Whether offline ticking is enabled. + * @param maxOfflineTime The maximum offline time allowed. + * @param enableSeason Whether seasons are enabled. + * @param autoSeasonChange Whether season change is automatic. + * @param seasonDuration The duration of each season. + * @param cropPerChunk The maximum number of crops per chunk. + * @param potPerChunk The maximum number of pots per chunk. + * @param sprinklerPerChunk The maximum number of sprinklers per chunk. + * @param randomTickSpeed The random tick speed. + */ private WorldSetting( boolean enableScheduler, int minTickUnit, @@ -75,6 +100,28 @@ private WorldSetting( this.tickSprinklerRandomly = tickSprinklerRandomly; } + /** + * Factory method to create a new instance of WorldSetting. + * + * @param enableScheduler Whether the scheduler is enabled. + * @param minTickUnit The minimum unit of tick. + * @param tickCropRandomly Whether crops are ticked randomly. + * @param tickCropInterval The interval for ticking crops. + * @param tickPotRandomly Whether pots are ticked randomly. + * @param tickPotInterval The interval for ticking pots. + * @param tickSprinklerRandomly Whether sprinklers are ticked randomly. + * @param tickSprinklerInterval The interval for ticking sprinklers. + * @param offlineGrow Whether offline ticking is enabled. + * @param maxOfflineTime The maximum offline time allowed. + * @param enableSeason Whether seasons are enabled. + * @param autoSeasonChange Whether season change is automatic. + * @param seasonDuration The duration of each season. + * @param cropPerChunk The maximum number of crops per chunk. + * @param potPerChunk The maximum number of pots per chunk. + * @param sprinklerPerChunk The maximum number of sprinklers per chunk. + * @param randomTickSpeed The random tick speed. + * @return A new WorldSetting instance. + */ public static WorldSetting of( boolean enableScheduler, int minTickUnit, @@ -115,74 +162,164 @@ public static WorldSetting of( ); } + /** + * Checks if the scheduler is enabled. + * + * @return true if the scheduler is enabled, false otherwise. + */ public boolean enableScheduler() { return enableScheduler; } + /** + * Gets the minimum tick unit. + * + * @return The minimum tick unit. + */ public int minTickUnit() { return minTickUnit; } + /** + * Gets the interval for ticking crops. + * + * @return The tick interval for crops. + */ public int tickCropInterval() { return tickCropInterval; } + /** + * Gets the interval for ticking pots. + * + * @return The tick interval for pots. + */ public int tickPotInterval() { return tickPotInterval; } + /** + * Gets the interval for ticking sprinklers. + * + * @return The tick interval for sprinklers. + */ public int tickSprinklerInterval() { return tickSprinklerInterval; } + /** + * Checks if offline ticking is enabled. + * + * @return true if offline ticking is enabled, false otherwise. + */ public boolean offlineTick() { return offlineTick; } + /** + * Checks if seasons are enabled. + * + * @return true if seasons are enabled, false otherwise. + */ public boolean enableSeason() { return enableSeason; } + /** + * Checks if automatic season change is enabled. + * + * @return true if automatic season change is enabled, false otherwise. + */ public boolean autoSeasonChange() { return autoSeasonChange; } + /** + * Gets the duration of each season. + * + * @return The duration of each season. + */ public int seasonDuration() { return seasonDuration; } + /** + * Gets the maximum number of pots per chunk. + * + * @return The maximum number of pots per chunk. + */ public int potPerChunk() { return potPerChunk; } + /** + * Gets the maximum number of crops per chunk. + * + * @return The maximum number of crops per chunk. + */ public int cropPerChunk() { return cropPerChunk; } + /** + * Gets the maximum number of sprinklers per chunk. + * + * @return The maximum number of sprinklers per chunk. + */ public int sprinklerPerChunk() { return sprinklerPerChunk; } + /** + * Gets the random tick speed. + * + * @return The random tick speed. + */ public int randomTickSpeed() { return randomTickSpeed; } + /** + * Checks if crops are ticked randomly. + * + * @return true if crops are ticked randomly, false otherwise. + */ public boolean randomTickCrop() { return tickCropRandomly; } + /** + * Checks if sprinklers are ticked randomly. + * + * @return true if sprinklers are ticked randomly, false otherwise. + */ public boolean randomTickSprinkler() { return tickSprinklerRandomly; } + /** + * Checks if pots are ticked randomly. + * + * @return true if pots are ticked randomly, false otherwise. + */ public boolean randomTickPot() { return tickPotRandomly; } + /** + * Gets the maximum offline time allowed. + * + * @return The maximum offline time. + */ public int maxOfflineTime() { return maxOfflineTime; } + /** + * Creates a clone of this WorldSetting instance. + * + * @return A cloned instance of WorldSetting. + */ @Override public WorldSetting clone() { try { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java index 82272b03d..5b0f466ff 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/adaptor/WorldAdaptor.java @@ -20,43 +20,99 @@ import net.momirealms.customcrops.api.core.world.*; import org.jetbrains.annotations.Nullable; +/** + * Interface defining methods for adapting different types of worlds (e.g., Bukkit, Slime) for use with CustomCrops. + * This adaptor provides methods to load and save regions and chunks, handle world-specific data, and interact with + * various world implementations. + * + * @param The type of the world that this adaptor supports. + */ public interface WorldAdaptor extends Comparable> { int BUKKIT_WORLD_PRIORITY = 100; int SLIME_WORLD_PRIORITY = 200; - + /** + * Loads extra data associated with the given world. + * + * @param world The world to load data for. + * @return The loaded {@link WorldExtraData} containing extra world-specific information. + */ WorldExtraData loadExtraData(W world); + /** + * Saves extra data for the given CustomCrops world instance. + * + * @param world The CustomCrops world instance whose extra data is to be saved. + */ void saveExtraData(CustomCropsWorld world); /** - * Load the region from file or cache + * Loads a region from the file or cache. Creates a new region if it doesn't exist and createIfNotExist is true. + * + * @param world The CustomCrops world instance to which the region belongs. + * @param pos The position of the region to be loaded. + * @param createIfNotExist If true, creates the region if it does not exist. + * @return The loaded {@link CustomCropsRegion}, or null if the region could not be loaded and createIfNotExist is false. */ @Nullable CustomCropsRegion loadRegion(CustomCropsWorld world, RegionPos pos, boolean createIfNotExist); /** - * Load the chunk from file or cache + * Loads a chunk from the file or cache. Creates a new chunk if it doesn't exist and createIfNotExist is true. + * + * @param world The CustomCrops world instance to which the chunk belongs. + * @param pos The position of the chunk to be loaded. + * @param createIfNotExist If true, creates the chunk if it does not exist. + * @return The loaded {@link CustomCropsChunk}, or null if the chunk could not be loaded and createIfNotExist is false. */ @Nullable CustomCropsChunk loadChunk(CustomCropsWorld world, ChunkPos pos, boolean createIfNotExist); /** - * Unload the region to file or cache + * Saves the specified region to a file or cache. + * + * @param world The CustomCrops world instance to which the region belongs. + * @param region The region to be saved. */ void saveRegion(CustomCropsWorld world, CustomCropsRegion region); + /** + * Saves the specified chunk to a file or cache. + * + * @param world The CustomCrops world instance to which the chunk belongs. + * @param chunk The chunk to be saved. + */ void saveChunk(CustomCropsWorld world, CustomCropsChunk chunk); + /** + * Retrieves the name of the given world. + * + * @param world The world instance. + * @return The name of the world. + */ String getName(W world); + /** + * Gets the world instance by its name. + * + * @param worldName The name of the world to retrieve. + * @return The world instance, or null if no world with the given name is found. + */ @Nullable W getWorld(String worldName); + /** + * Adapts the given object to a CustomCropsWorld instance if possible. + * + * @param world The object to adapt. + * @return The adapted {@link CustomCropsWorld} instance. + */ CustomCropsWorld adapt(Object world); - long getWorldFullTime(W world); - + /** + * Gets the priority of this world adaptor. Adaptors with lower priority values are considered before those with higher values. + * + * @return The priority value of this adaptor. + */ int priority(); - } diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/DynamicText.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/DynamicText.java deleted file mode 100644 index 816df3437..000000000 --- a/api/src/main/java/net/momirealms/customcrops/api/misc/value/DynamicText.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.misc.value; - -import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class DynamicText { - - private final Player owner; - private String originalValue; - private String latestValue; - private String[] placeholders; - - public DynamicText(Player owner, String rawValue) { - this.owner = owner; - analyze(rawValue); - } - - private void analyze(String value) { - // Analyze the provided text to find and replace placeholders with '%s'. - // Store the original value, placeholders, and the initial latest value. - List placeholdersOwner = new ArrayList<>(BukkitPlaceholderManager.getInstance().resolvePlaceholders(value)); - String origin = value; - for (String placeholder : placeholdersOwner) { - origin = origin.replace(placeholder, "%s"); - } - originalValue = origin; - placeholders = placeholdersOwner.toArray(new String[0]); - latestValue = originalValue; - } - - public String getLatestValue() { - return latestValue; - } - - public boolean update(Map placeholders) { - String string = originalValue; - if (this.placeholders.length != 0) { - BukkitPlaceholderManager bukkitPlaceholderManager = BukkitPlaceholderManager.getInstance(); - if ("%s".equals(originalValue)) { - string = bukkitPlaceholderManager.parseSingle(owner, this.placeholders[0], placeholders); - } else { - Object[] values = new String[this.placeholders.length]; - for (int i = 0; i < this.placeholders.length; i++) { - values[i] = bukkitPlaceholderManager.parseSingle(owner, this.placeholders[i], placeholders); - } - string = String.format(originalValue, values); - } - } - if (!latestValue.equals(string)) { - latestValue = string; - return true; - } - return false; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/MathValue.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/MathValue.java index f31b859b8..410ccda7a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/misc/value/MathValue.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/MathValue.java @@ -22,7 +22,7 @@ /** * The MathValue interface represents a mathematical value that can be evaluated * within a specific context. This interface allows for the evaluation of mathematical - * expressions or plain numerical values in the context of custom fishing mechanics. + * expressions or plain numerical values in the context of custom crops mechanics. * * @param the type of the holder object for the context */ diff --git a/api/src/main/java/net/momirealms/customcrops/api/misc/value/TextValue.java b/api/src/main/java/net/momirealms/customcrops/api/misc/value/TextValue.java index 3366bc476..373a2a73d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/misc/value/TextValue.java +++ b/api/src/main/java/net/momirealms/customcrops/api/misc/value/TextValue.java @@ -26,7 +26,7 @@ /** * The TextValue interface represents a text value that can be rendered * within a specific context. This interface allows for the rendering of - * placeholder-based or plain text values in the context of custom fishing mechanics. + * placeholder-based or plain text values in the context of custom crops mechanics. * * @param the type of the holder object for the context */ diff --git a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java index 88796593a..13b348cdf 100644 --- a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java +++ b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java @@ -158,11 +158,6 @@ public String getName(SlimeWorld world) { return world.getName(); } - @Override - public long getWorldFullTime(SlimeWorld world) { - return Objects.requireNonNull(Bukkit.getWorld(world.getName())).getFullTime(); - } - @Override public int priority() { return SLIME_WORLD_PRIORITY; 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 df85741e6..01f08e259 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -61,6 +61,7 @@ import java.nio.file.Path; import java.util.List; import java.util.function.Consumer; +import java.util.function.Supplier; public class BukkitCustomCropsPluginImpl extends BukkitCustomCropsPlugin { @@ -91,6 +92,12 @@ public void debug(Object message) { this.debugger.accept(message); } + @Override + public void debug(Supplier message) { + if (this.debugger != null) + this.debugger.accept(message.get()); + } + @Override public InputStream getResourceStream(String filePath) { return getBoostrap().getResource(filePath); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java index cd8748d36..baa974ebf 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -266,7 +266,8 @@ public class ConfigType { .storage(section.getInt("storage", 4)) .infinite(section.getBoolean("infinite", false)) .twoDItem(section.getString("2D-item")) - .sprinklingAmount(section.getInt("water", 1)) + .wateringAmount(section.getInt("water", 1)) + .sprinklingAmount(section.getInt("sprinkling", 1)) .threeDItem(section.getString("3D-item")) .threeDItemWithWater(section.getString("3D-item-with-water")) .wateringMethods(manager.getWateringMethods(section.getSection("fill-method"))) 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 becf60bb4..d8076734f 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 @@ -212,11 +212,6 @@ public String getName(World world) { return world.getName(); } - @Override - public long getWorldFullTime(World world) { - return world.getFullTime(); - } - @Override public int priority() { return BUKKIT_WORLD_PRIORITY; 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 90c6a94ab..385b17193 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 @@ -169,6 +169,7 @@ public void place(@NotNull Location location, @NotNull ExistenceForm form, @NotN } } + @NotNull @Override public FurnitureRotation remove(@NotNull Location location, @NotNull ExistenceForm form) { switch (form) { @@ -216,6 +217,7 @@ public void removeBlock(@NotNull Location location) { } } + @NotNull @Override public FurnitureRotation removeFurniture(@NotNull Location location) { Collection entities = location.getWorld().getNearbyEntities(LocationUtils.toSurfaceCenterLocation(location), 0.5,0.25,0.5); @@ -229,7 +231,7 @@ public FurnitureRotation removeFurniture(@NotNull Location location) { } } } - return rotation; + return rotation == null ? FurnitureRotation.NONE : rotation; } @NotNull @@ -329,7 +331,7 @@ public ItemStack build(Player player, @NotNull String id) { } @Override - public Item wrap(ItemStack itemStack) { + public Item wrap(@NotNull ItemStack itemStack) { return factory.wrap(itemStack); } 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 02a5cbb38..df7364a73 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 @@ -43,7 +43,7 @@ public class BukkitWorldManager implements WorldManager, Listener { private final BukkitCustomCropsPlugin plugin; - private final Set> adaptors = new TreeSet<>(); + private final TreeSet> adaptors = new TreeSet<>(); private final ConcurrentHashMap> worlds = new ConcurrentHashMap<>(); private final HashMap worldSettings = new HashMap<>(); private WorldSetting defaultWorldSetting; @@ -290,7 +290,7 @@ public boolean isWorldLoaded(World world) { } @Override - public Set> adaptors() { + public TreeSet> adaptors() { return adaptors; } From a1885e1dda34cf33c964469c46845a0c943776fe Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 02:49:18 +0800 Subject: [PATCH 078/329] offline ticks --- .../api/action/AbstractActionManager.java | 8 +- .../api/core/AbstractCustomEventListener.java | 4 +- .../customcrops/api/core/ConfigManager.java | 10 +-- .../customcrops/api/core/Registries.java | 6 +- .../core/block/AbstractCustomCropsBlock.java | 4 +- .../customcrops/api/core/block/CropBlock.java | 20 ++--- .../api/core/block/CustomCropsBlock.java | 4 +- .../api/core/block/GreenhouseBlock.java | 12 +-- .../customcrops/api/core/block/PotBlock.java | 29 +++---- .../api/core/block/ScarecrowBlock.java | 12 +-- .../api/core/block/SprinklerBlock.java | 17 ++-- .../api/core/item/FertilizerItem.java | 4 +- .../customcrops/api/core/item/SeedItem.java | 5 +- .../api/core/item/SprinklerItem.java | 5 +- .../api/core/item/WateringCanItem.java | 5 +- .../api/core/world/CustomCropsChunk.java | 18 ++-- .../api/core/world/CustomCropsChunkImpl.java | 84 +++++++++++++------ .../api/core/world/CustomCropsWorldImpl.java | 11 +-- .../api/core/world/WorldManager.java | 1 - .../api/core/world/WorldSetting.java | 15 ++++ .../api/event/FertilizerUseEvent.java | 2 +- .../AbstractRequirementManager.java | 2 +- .../adaptor/asp_r1/SlimeWorldAdaptorR1.java | 5 +- gradle.properties | 2 +- .../bukkit/BukkitCustomCropsPluginImpl.java | 5 +- .../bukkit/action/PlayerActionManager.java | 8 +- .../command/feature/ForceTickCommand.java | 8 +- .../bukkit/config/BukkitConfigManager.java | 8 +- .../customcrops/bukkit/config/ConfigType.java | 4 +- .../bukkit/item/BukkitItemManager.java | 4 +- .../bukkit/world/BukkitWorldManager.java | 29 ++++++- plugin/src/main/resources/config.yml | 5 ++ 32 files changed, 215 insertions(+), 141 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java index da5f84c90..3301e7344 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java @@ -24,12 +24,14 @@ import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.block.*; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.block.PotBlock; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; -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.CustomCropsChunk; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java index b42624e2e..c215d9edc 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -21,11 +21,11 @@ import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; -import net.momirealms.customcrops.api.core.block.*; -import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.block.CropBlock; import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal; import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java index 0b2e1ee36..b3a36f89b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/ConfigManager.java @@ -12,15 +12,15 @@ import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; import dev.dejvokep.boostedyaml.utils.format.NodeRole; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; -import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; -import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.mechanic.crop.DeathCondition; import net.momirealms.customcrops.api.core.mechanic.crop.GrowCondition; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; -import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal; -import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.misc.water.WateringMethod; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java index 4745248d8..ee6f0b274 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/Registries.java @@ -17,13 +17,13 @@ package net.momirealms.customcrops.api.core; -import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; -import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; -import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.item.CustomCropsItem; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.common.annotation.DoNotUse; import net.momirealms.customcrops.common.util.Key; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java index 0f8ad3aa6..cef84aa48 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java @@ -78,11 +78,11 @@ protected boolean canTick(CustomCropsBlockState state, int interval) { } @Override - public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { } @Override - public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { } @Override 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 0f2852f62..30e1ebdfa 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 @@ -23,9 +23,9 @@ import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.mechanic.crop.*; import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.core.mechanic.crop.*; import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; @@ -58,16 +58,16 @@ public CropBlock() { } @Override - public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { if (!world.setting().randomTickCrop() && canTick(state, world.setting().tickCropInterval())) { - tickCrop(state, world, location); + tickCrop(state, world, location, offlineTick); } } @Override - public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { if (world.setting().randomTickCrop() && canTick(state, world.setting().tickCropInterval())) { - tickCrop(state, world, location); + tickCrop(state, world, location, offlineTick); } } @@ -273,20 +273,20 @@ public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, } CropConfig cropConfig = configList.get(0); CropStageConfig stageConfig = cropConfig.stageByID(stageID); + assert stageConfig != null; int point = stageConfig.point(); CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState(); point(state, point); id(state, cropConfig.id()); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); return state; } - private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offline) { CropConfig config = config(state); if (config == null) { BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Crop data is removed at location[" + world.worldName() + "," + location + "] because the crop config[" + id(state) + "] has been removed."); @@ -306,7 +306,7 @@ private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Po } } - Context context = Context.block(state); + Context context = Context.block(state).arg(ContextKeys.OFFLINE, offline); Location bukkitLocation = location.toLocation(bukkitWorld); context.arg(ContextKeys.LOCATION, bukkitLocation); for (DeathCondition deathCondition : config.deathConditions()) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java index efcf9fb37..a7a9c25b0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java @@ -34,9 +34,9 @@ public interface CustomCropsBlock { CustomCropsBlockState createBlockState(CompoundMap data); - void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location); + void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick); - void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location); + void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick); void onInteract(WrappedInteractEvent event); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java index 4728083a5..3bb3d8bad 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java @@ -40,12 +40,13 @@ public GreenhouseBlock() { } @Override - public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { //tickGreenhouse(world, location); } @Override - public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { + if (offlineTick) return; tickGreenhouse(world, location); } @@ -89,9 +90,8 @@ public void onPlace(WrappedPlaceEvent event) { Pos3 pos3 = Pos3.from(event.location()); CustomCropsWorld world = event.world(); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); } @@ -108,7 +108,7 @@ public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) } CustomCropsBlockState state = createBlockState(); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 362b22c38..0e6652dbb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -23,11 +23,11 @@ import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; -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; @@ -67,16 +67,16 @@ public PotBlock() { } @Override - public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { if (!world.setting().randomTickPot() && canTick(state, world.setting().tickPotInterval())) { - tickPot(state, world, location); + tickPot(state, world, location, offlineTick); } } @Override - public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { if (world.setting().randomTickPot() && canTick(state, world.setting().tickPotInterval())) { - tickPot(state, world, location); + tickPot(state, world, location, offlineTick); } } @@ -211,9 +211,8 @@ public void onPlace(WrappedPlaceEvent event) { } world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); ActionManager.trigger(context, config.placeActions()); @@ -324,15 +323,14 @@ public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, id(state, potConfig.id()); water(state, potConfig.isWet(blockID) ? 1 : 0); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); return state; } - private void tickPot(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + private void tickPot(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offline) { PotConfig config = config(state); if (config == null) { BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Pot data is removed at location[" + world.worldName() + "," + location + "] because the pot config[" + id(state) + "] has been removed."); @@ -411,7 +409,10 @@ private void tickPot(CustomCropsBlockState state, CustomCropsWorld world, Pos } ActionManager.trigger(Context.block(state) - .arg(ContextKeys.LOCATION, location.toLocation(bukkitWorld)), config.tickActions()); + .arg(ContextKeys.OFFLINE, offline) + .arg(ContextKeys.LOCATION, location.toLocation(bukkitWorld)), + config.tickActions() + ); } public int water(CustomCropsBlockState state) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java index 1aad9b266..80aea7acb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java @@ -40,12 +40,13 @@ public ScarecrowBlock() { } @Override - public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { //tickScarecrow(world, location); } @Override - public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { + if (offlineTick) return; tickScarecrow(world, location); } @@ -89,9 +90,8 @@ public void onPlace(WrappedPlaceEvent event) { Pos3 pos3 = Pos3.from(event.location()); CustomCropsWorld world = event.world(); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); } @@ -108,7 +108,7 @@ public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) } CustomCropsBlockState state = createBlockState(); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java index 29d62d9d1..86c3989be 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -57,16 +57,16 @@ public SprinklerBlock() { } @Override - public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void randomTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { if (!world.setting().randomTickSprinkler() && canTick(state, world.setting().tickSprinklerInterval())) { - tickSprinkler(state, world, location); + tickSprinkler(state, world, location, offlineTick); } } @Override - public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offlineTick) { if (world.setting().randomTickSprinkler() && canTick(state, world.setting().tickSprinklerInterval())) { - tickSprinkler(state, world, location); + tickSprinkler(state, world, location, offlineTick); } } @@ -231,15 +231,14 @@ public CustomCropsBlockState fixOrGetState(CustomCropsWorld world, Pos3 pos3, id(state, sprinklerConfig.id()); water(state, blockID.equals(sprinklerConfig.threeDItemWithWater()) ? 1 : 0); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); return state; } - private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location) { + private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld world, Pos3 location, boolean offline) { SprinklerConfig config = config(state); if (config == null) { BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Sprinkler data is removed at location[" + world.worldName() + "," + location + "] because the sprinkler config[" + id(state) + "] has been removed."); @@ -257,7 +256,7 @@ private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld worl updateState = false; } - Context context = Context.block(state); + Context context = Context.block(state).arg(ContextKeys.OFFLINE, offline); World bukkitWorld = world.bukkitWorld(); Location bukkitLocation = location.toLocation(bukkitWorld); context.arg(ContextKeys.LOCATION, bukkitLocation); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java index cfd90bb10..d267945eb 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java @@ -26,11 +26,11 @@ import net.momirealms.customcrops.api.core.InteractionResult; import net.momirealms.customcrops.api.core.Registries; import net.momirealms.customcrops.api.core.block.CropBlock; -import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.block.PotBlock; -import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java index a4dc56980..4d780b019 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java @@ -117,9 +117,8 @@ public InteractionResult interactAt(WrappedInteractEvent event) { BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(seedLocation), form, stageID, cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE); cropBlock.point(state, point); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java index 8ed8fac0d..c78c3346d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java @@ -110,9 +110,8 @@ public InteractionResult interactAt(WrappedInteractEvent event) { // place the sprinkler BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(targetLocation), config.existenceForm(), config.threeDItem(), FurnitureRotation.NONE); world.addBlockState(pos3, state).ifPresent(previous -> { - BukkitCustomCropsPlugin.getInstance().debug( - "Overwrite old data with " + state + - " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous + BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state + + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous ); }); ActionManager.trigger(context, config.placeActions()); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java index 08c1a211c..be7f5febe 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java @@ -24,9 +24,10 @@ import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.block.*; -import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.block.PotBlock; +import net.momirealms.customcrops.api.core.block.SprinklerBlock; import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java index a84c7f4b4..ca1f44cc5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunk.java @@ -107,14 +107,14 @@ public interface CustomCropsChunk { * * @return The unloaded time in seconds. */ - int unloadedSeconds(); + int lazySeconds(); /** * Sets the time in seconds since the chunk was unloaded. * - * @param unloadedSeconds The unloaded time to set. + * @param lazySeconds The unloaded time to set. */ - void unloadedSeconds(int unloadedSeconds); + void lazySeconds(int lazySeconds); /** * Gets the last time the chunk was loaded. @@ -126,7 +126,7 @@ public interface CustomCropsChunk { /** * Updates the last loaded time to the current time. */ - void updateLastLoadedTime(); + void updateLastUnloadTime(); /** * Gets the time in milliseconds since the chunk was loaded. @@ -203,11 +203,6 @@ public interface CustomCropsChunk { */ Optional removeSection(int sectionID); - /** - * Resets the unloaded time counter to zero. - */ - void resetUnloadedSeconds(); - /** * Checks if the chunk can be pruned (removed from memory or storage). * @@ -222,6 +217,11 @@ public interface CustomCropsChunk { */ boolean isOfflineTaskNotified(); + /** + * notify offline tasks + */ + void notifyOfflineTask(); + /** * Gets the queue of delayed tick tasks for this chunk. * diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java index c0d79ae5a..d7326dfcd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsChunkImpl.java @@ -32,30 +32,31 @@ public class CustomCropsChunkImpl implements CustomCropsChunk { private final ConcurrentHashMap loadedSections; private final PriorityQueue queue; private final Set tickedBlocks; - private long lastLoadedTime; + private long lastUnloadTime; private int loadedSeconds; - private int unloadedSeconds; + private int lazySeconds; private boolean notified; private boolean isLoaded; private boolean forceLoad; + // new chunk protected CustomCropsChunkImpl(CustomCropsWorld world, ChunkPos chunkPos) { this.world = world; this.chunkPos = chunkPos; this.loadedSections = new ConcurrentHashMap<>(16); this.queue = new PriorityQueue<>(); - this.unloadedSeconds = 0; + this.lazySeconds = 0; this.tickedBlocks = Collections.synchronizedSet(new HashSet<>()); - this.updateLastLoadedTime(); this.notified = true; this.isLoaded = false; + this.updateLastUnloadTime(); } protected CustomCropsChunkImpl( CustomCropsWorld world, ChunkPos chunkPos, int loadedSeconds, - long lastLoadedTime, + long lastUnloadTime, ConcurrentHashMap loadedSections, PriorityQueue queue, HashSet tickedBlocks @@ -63,11 +64,13 @@ protected CustomCropsChunkImpl( this.world = world; this.chunkPos = chunkPos; this.loadedSections = loadedSections; - this.lastLoadedTime = lastLoadedTime; + this.lastUnloadTime = lastUnloadTime; this.loadedSeconds = loadedSeconds; this.queue = queue; - this.unloadedSeconds = 0; + this.lazySeconds = 0; this.tickedBlocks = Collections.synchronizedSet(tickedBlocks); + this.notified = false; + this.isLoaded = false; } @Override @@ -85,6 +88,7 @@ public void load(boolean loadBukkitChunk) { if (!isLoaded()) { if (((CustomCropsWorldImpl) world).loadChunk(this)) { this.isLoaded = true; + this.lazySeconds = 0; } if (loadBukkitChunk && !this.world.bukkitWorld().isChunkLoaded(chunkPos.x(), chunkPos.z())) { this.world.bukkitWorld().getChunkAt(chunkPos.x(), chunkPos.z()); @@ -97,6 +101,8 @@ public void unload(boolean lazy) { if (isLoaded() && !isForceLoaded()) { if (((CustomCropsWorldImpl) world).unloadChunk(this, lazy)) { this.isLoaded = false; + this.notified = false; + this.lazySeconds = 0; } } } @@ -140,8 +146,8 @@ public void timer() { this.queue.clear(); this.arrangeTasks(interval); } - scheduledTick(); - randomTick(setting.randomTickSpeed()); + scheduledTick(false); + randomTick(setting.randomTickSpeed(), false); } private void arrangeTasks(int unit) { @@ -157,7 +163,7 @@ private void arrangeTasks(int unit) { } } - private void scheduledTick() { + private void scheduledTick(boolean offline) { while (!queue.isEmpty() && queue.peek().getTime() <= loadedSeconds) { DelayedTickTask task = queue.poll(); if (task != null) { @@ -165,13 +171,13 @@ private void scheduledTick() { CustomCropsSection section = loadedSections.get(pos.sectionID()); if (section != null) { Optional block = section.getBlockState(pos); - block.ifPresent(state -> state.type().scheduledTick(state, world, pos.toPos3(chunkPos))); + block.ifPresent(state -> state.type().scheduledTick(state, world, pos.toPos3(chunkPos), offline)); } } } } - private void randomTick(int randomTickSpeed) { + private void randomTick(int randomTickSpeed, boolean offline) { ThreadLocalRandom random = ThreadLocalRandom.current(); for (CustomCropsSection section : loadedSections.values()) { int sectionID = section.getSectionID(); @@ -182,34 +188,34 @@ private void randomTick(int randomTickSpeed) { int z = random.nextInt(16); BlockPos pos = new BlockPos(x,y,z); Optional block = section.getBlockState(pos); - block.ifPresent(state -> state.type().randomTick(state, world, pos.toPos3(chunkPos))); + block.ifPresent(state -> state.type().randomTick(state, world, pos.toPos3(chunkPos), offline)); } } } @Override - public int unloadedSeconds() { - return unloadedSeconds; + public int lazySeconds() { + return lazySeconds; } @Override - public void unloadedSeconds(int unloadedSeconds) { - this.unloadedSeconds = unloadedSeconds; + public void lazySeconds(int lazySeconds) { + this.lazySeconds = lazySeconds; } @Override public long lastLoadedTime() { - return lastLoadedTime; + return lastUnloadTime; } @Override - public void updateLastLoadedTime() { - this.lastLoadedTime = System.currentTimeMillis(); + public void updateLastUnloadTime() { + this.lastUnloadTime = System.currentTimeMillis(); } @Override public int loadedMilliSeconds() { - return (int) (System.currentTimeMillis() - lastLoadedTime); + return (int) (System.currentTimeMillis() - lastUnloadTime); } @NotNull @@ -266,12 +272,6 @@ public Optional removeSection(int sectionID) { return Optional.ofNullable(loadedSections.remove(sectionID)); } - @Override - public void resetUnloadedSeconds() { - this.unloadedSeconds = 0; - this.notified = false; - } - @Override public boolean canPrune() { return loadedSections.isEmpty(); @@ -282,6 +282,36 @@ public boolean isOfflineTaskNotified() { return notified; } + @Override + public void notifyOfflineTask() { + if (isOfflineTaskNotified()) return; + this.notified = true; + long current = System.currentTimeMillis(); + int offlineTimeInSeconds = (int) (current - lastLoadedTime()) / 1000; + WorldSetting setting = world.setting(); + offlineTimeInSeconds = Math.min(offlineTimeInSeconds, setting.maxOfflineTime()); + int minTickUnit = setting.minTickUnit(); + int randomTickSpeed = setting.randomTickSpeed(); + int threshold = setting.maxLoadingTime(); + int i = 0; + long time1 = System.currentTimeMillis(); + while (i < offlineTimeInSeconds) { + this.loadedSeconds++; + if (this.loadedSeconds >= minTickUnit) { + this.loadedSeconds = 0; + this.tickedBlocks.clear(); + this.queue.clear(); + this.arrangeTasks(minTickUnit); + } + scheduledTick(true); + randomTick(randomTickSpeed, true); + i++; + if (System.currentTimeMillis() - time1 > threshold) { + break; + } + } + } + @Override public PriorityQueue tickTaskQueue() { return queue; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java index ca65d0e23..16d785961 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorldImpl.java @@ -272,11 +272,11 @@ private void saveLazyChunks() { ArrayList chunksToSave = new ArrayList<>(); for (Map.Entry lazyEntry : this.lazyChunks.entrySet()) { CustomCropsChunk chunk = lazyEntry.getValue(); - int sec = chunk.unloadedSeconds() + 1; + int sec = chunk.lazySeconds() + 1; if (sec >= 30) { chunksToSave.add(chunk); } else { - chunk.unloadedSeconds(sec); + chunk.lazySeconds(sec); } } for (CustomCropsChunk chunk : chunksToSave) { @@ -309,9 +309,6 @@ public void setting(WorldSetting setting) { this.setting = setting; } - /* - * Chunks - */ @Nullable public CustomCropsChunk removeLazyChunk(ChunkPos chunkPos) { return this.lazyChunks.remove(chunkPos); @@ -355,7 +352,7 @@ public boolean unloadChunk(CustomCropsChunk chunk, boolean lazy) { return false; } this.loadedChunks.remove(chunk.chunkPos()); - chunk.updateLastLoadedTime(); + chunk.updateLastUnloadTime(); if (lazy) { this.lazyChunks.put(pos, chunk); } else { @@ -368,7 +365,7 @@ public boolean unloadChunk(CustomCropsChunk chunk, boolean lazy) { public boolean unloadChunk(ChunkPos pos, boolean lazy) { CustomCropsChunk removed = this.loadedChunks.remove(pos); if (removed != null) { - removed.updateLastLoadedTime(); + removed.updateLastUnloadTime(); if (lazy) { this.lazyChunks.put(pos, removed); } else { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java index 8085cd9f7..f3ab9d031 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldManager.java @@ -23,7 +23,6 @@ import org.bukkit.World; import java.util.Optional; -import java.util.Set; import java.util.TreeSet; /** diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java index cf87fd16a..96e337b63 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/WorldSetting.java @@ -37,6 +37,7 @@ public class WorldSetting implements Cloneable { private final int sprinklerPerChunk; private final int randomTickSpeed; private final int maxOfflineTime; + private final int maxLoadingTime; private final boolean tickCropRandomly; private final boolean tickPotRandomly; private final boolean tickSprinklerRandomly; @@ -54,6 +55,7 @@ public class WorldSetting implements Cloneable { * @param tickSprinklerInterval The interval for ticking sprinklers. * @param offlineTick Whether offline ticking is enabled. * @param maxOfflineTime The maximum offline time allowed. + * @param maxLoadingTime The maximum time allowed to load. * @param enableSeason Whether seasons are enabled. * @param autoSeasonChange Whether season change is automatic. * @param seasonDuration The duration of each season. @@ -73,6 +75,7 @@ private WorldSetting( int tickSprinklerInterval, boolean offlineTick, int maxOfflineTime, + int maxLoadingTime, boolean enableSeason, boolean autoSeasonChange, int seasonDuration, @@ -88,6 +91,7 @@ private WorldSetting( this.tickSprinklerInterval = tickSprinklerInterval; this.offlineTick = offlineTick; this.maxOfflineTime = maxOfflineTime; + this.maxLoadingTime = maxLoadingTime; this.enableSeason = enableSeason; this.autoSeasonChange = autoSeasonChange; this.seasonDuration = seasonDuration; @@ -133,6 +137,7 @@ public static WorldSetting of( int tickSprinklerInterval, boolean offlineGrow, int maxOfflineTime, + int maxLoadingTime, boolean enableSeason, boolean autoSeasonChange, int seasonDuration, @@ -152,6 +157,7 @@ public static WorldSetting of( tickSprinklerInterval, offlineGrow, maxOfflineTime, + maxLoadingTime, enableSeason, autoSeasonChange, seasonDuration, @@ -216,6 +222,15 @@ public boolean offlineTick() { return offlineTick; } + /** + * Gets the max time allowed to load a chunk + * + * @return The max loading time + */ + public int maxLoadingTime() { + return maxLoadingTime; + } + /** * Checks if seasons are enabled. * diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java index 32212e06b..98a70c23d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java @@ -17,8 +17,8 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import org.bukkit.Location; import org.bukkit.entity.Player; 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 d52a8d301..1c19b184e 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 @@ -23,10 +23,10 @@ import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.core.ConfigManager; -import net.momirealms.customcrops.api.core.mechanic.crop.CrowAttack; import net.momirealms.customcrops.api.core.block.GreenhouseBlock; import net.momirealms.customcrops.api.core.block.PotBlock; 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; diff --git a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java index 13b348cdf..e329a7691 100644 --- a/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java +++ b/compatibility-asp-r1/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/asp_r1/SlimeWorldAdaptorR1.java @@ -32,7 +32,10 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.Method; -import java.util.*; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.PriorityQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; diff --git a/gradle.properties b/gradle.properties index b2e46075d..62c1f7d00 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,7 +15,7 @@ asm_commons_version=9.7 jar_relocator_version=1.7 adventure_bundle_version=4.17.0 adventure_platform_version=4.3.4 -sparrow_heart_version=0.40 +sparrow_heart_version=0.42 cloud_core_version=2.0.0-rc.2 cloud_services_version=2.0.0-rc.2 cloud_brigadier_version=2.0.0-beta.9 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 01f08e259..549af95e9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -21,7 +21,10 @@ import net.momirealms.customcrops.api.core.ConfigManager; import net.momirealms.customcrops.api.core.SimpleRegistryAccess; import net.momirealms.customcrops.api.core.block.*; -import net.momirealms.customcrops.api.core.item.*; +import net.momirealms.customcrops.api.core.item.FertilizerItem; +import net.momirealms.customcrops.api.core.item.SeedItem; +import net.momirealms.customcrops.api.core.item.SprinklerItem; +import net.momirealms.customcrops.api.core.item.WateringCanItem; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java index dfb286513..1e39247ab 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/action/PlayerActionManager.java @@ -472,8 +472,8 @@ private void registerForceTickAction() { Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> { CustomCropsBlock customCropsBlock = state.type(); - customCropsBlock.randomTick(state, world, pos3); - customCropsBlock.scheduledTick(state, world, pos3); + customCropsBlock.randomTick(state, world, pos3, false); + customCropsBlock.scheduledTick(state, world, pos3, false); if (customCropsBlock instanceof SprinklerBlock sprinklerBlock) { int water = sprinklerBlock.water(state); SprinklerConfig config = sprinklerBlock.config(state); @@ -490,8 +490,8 @@ private void registerForceTickAction() { Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> { CustomCropsBlock customCropsBlock = state.type(); - customCropsBlock.randomTick(state, world, pos3); - customCropsBlock.scheduledTick(state, world, pos3); + customCropsBlock.randomTick(state, world, pos3, false); + customCropsBlock.scheduledTick(state, world, pos3, false); })); }, "tick"); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java index 0355b927c..6e43ac5ee 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/ForceTickCommand.java @@ -109,11 +109,11 @@ public Command.Builder assembleCommand(CommandManager customCropsBlock.randomTick(state, world, location, false)), + SCHEDULED_TICK((customCropsBlock, state, world, location) -> customCropsBlock.scheduledTick(state, world, location, false)), ALL((b, s, w, p) -> { - b.randomTick(s, w, p); - b.scheduledTick(s, w, p); + b.randomTick(s, w, p, false); + b.scheduledTick(s, w, p, false); }); private final QuadConsumer, Pos3> consumer; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java index e77a2488c..461eadcd1 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -30,13 +30,13 @@ import dev.dejvokep.boostedyaml.utils.format.NodeRole; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; -import net.momirealms.customcrops.api.core.mechanic.crop.DeathCondition; -import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.DeathCondition; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; +import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.common.helper.AdventureHelper; import net.momirealms.customcrops.common.locale.TranslationManager; import net.momirealms.customcrops.common.plugin.CustomCropsProperties; diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java index baa974ebf..7cb6144ce 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -27,10 +27,10 @@ import net.momirealms.customcrops.api.core.Registries; import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; -import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; -import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.misc.value.TextValue; 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 385b17193..995e5d901 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 @@ -512,7 +512,7 @@ public void handlePlayerPlace(Player player, Location location, String placedID, if (anyFurnitureID != null) { if (!customCropsBlockState.type().isBlockInstance(anyFurnitureID)) { world.removeBlockState(pos3); - plugin.debug("[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState); + plugin.debug(() -> "[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState); } else { event.setCancelled(true); return; @@ -521,7 +521,7 @@ public void handlePlayerPlace(Player player, Location location, String placedID, String anyBlockID = blockID(location); if (!customCropsBlockState.type().isBlockInstance(anyBlockID)) { world.removeBlockState(pos3); - plugin.debug("[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState); + plugin.debug(() -> "[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState); } else { event.setCancelled(true); return; 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 df7364a73..02905018b 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 @@ -194,7 +194,9 @@ public CustomCropsWorld loadWorld(World world) { adaptedWorld.setTicking(true); this.worlds.put(world.getName(), adaptedWorld); for (Chunk chunk : world.getLoadedChunks()) { - loadLoadedChunk(adaptedWorld, ChunkPos.fromBukkitChunk(chunk)); + ChunkPos pos = ChunkPos.fromBukkitChunk(chunk); + loadLoadedChunk(adaptedWorld, pos); + notifyOfflineUpdates(adaptedWorld, pos); } return adaptedWorld; } @@ -204,7 +206,13 @@ public void loadLoadedChunk(CustomCropsWorld world, ChunkPos pos) { if (world.isChunkLoaded(pos)) return; Optional customChunk = world.getChunk(pos); // don't load bukkit chunk again since it has been loaded - customChunk.ifPresent(customCropsChunk -> customCropsChunk.load(false)); + customChunk.ifPresent(customCropsChunk -> { + customCropsChunk.load(false); + }); + } + + public void notifyOfflineUpdates(CustomCropsWorld world, ChunkPos pos) { + world.getChunk(pos).ifPresent(CustomCropsChunk::notifyOfflineTask); } @Override @@ -251,12 +259,24 @@ public void onChunkUnload(ChunkUnloadEvent event) { public void onChunkLoad(ChunkLoadEvent event) { final Chunk chunk = event.getChunk(); final World world = event.getWorld(); - this.getWorld(world).ifPresent(customWorld -> loadLoadedChunk(customWorld, ChunkPos.fromBukkitChunk(chunk))); + this.getWorld(world).ifPresent(customWorld -> { + ChunkPos pos = ChunkPos.fromBukkitChunk(chunk); + loadLoadedChunk(customWorld, pos); + if (chunk.isEntitiesLoaded() && customWorld.setting().offlineTick()) { + notifyOfflineUpdates(customWorld, pos); + } + }); } @EventHandler public void onEntitiesLoad(EntitiesLoadEvent event) { - + final Chunk chunk = event.getChunk(); + final World world = event.getWorld(); + this.getWorld(world).ifPresent(customWorld -> { + if (customWorld.setting().offlineTick()) { + notifyOfflineUpdates(customWorld, ChunkPos.fromBukkitChunk(chunk)); + } + }); } public boolean isMechanicEnabled(World world) { @@ -329,6 +349,7 @@ private static WorldSetting sectionToWorldSetting(Section section) { section.getInt("sprinkler.tick-interval", 2), section.getBoolean("offline-tick.enable", false), section.getInt("offline-tick.max-offline-seconds", 1200), + section.getInt("offline-tick.max-loading-time", 100), section.getBoolean("season.enable", false), section.getBoolean("season.auto-alternation", false), section.getInt("season.duration", 28), diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 8e412ebab..71601ee1a 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -52,6 +52,11 @@ worlds: # as it may cause chunks that have been unloaded for a long time # taking long to load max-offline-seconds: 1200 + # The max time used in loading this chunk + # Sometimes a chunk might contain a lot of data and has not been loaded for a long time, + # thus taking a long time loading and causing unexpected issues + # This setting allows the plugin to forcefully interrupt the tick process if the time consumed has exceeded a certain value + max-loading-time: 100 #ms # Settings for crops crop: # [RANDOM_TICK] From 87ca9cbe1b265c196d8f84379cf5584640b4097c Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:11:12 +0800 Subject: [PATCH 079/329] implement antigrief --- .../api/core/AbstractCustomEventListener.java | 18 +++--- .../api/core/SimpleRegistryAccess.java | 14 +++-- .../customcrops/api/core/block/CropBlock.java | 16 +++-- .../common/helper/VersionHelper.java | 2 +- gradle.properties | 2 +- .../bukkit/BukkitCustomCropsPluginImpl.java | 60 +++++++++++-------- .../bukkit/item/BukkitItemManager.java | 23 +++++++ plugin/src/main/resources/plugin.yml | 26 +------- 8 files changed, 92 insertions(+), 69 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java index c215d9edc..2f676bb97 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -102,7 +102,7 @@ public void onInteractAir(PlayerInteractEvent event) { ); } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) public void onInteractBlock(PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; @@ -121,7 +121,7 @@ public void onInteractBlock(PlayerInteractEvent event) { ); } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) public void onInteractEntity(PlayerInteractAtEntityEvent event) { EntityType type = event.getRightClicked().getType(); if (entities.contains(type)) { @@ -135,7 +135,7 @@ public void onInteractEntity(PlayerInteractAtEntityEvent event) { ); } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) public void onPlaceBlock(BlockPlaceEvent event) { Block block = event.getBlock(); if (blocks.contains(block.getType())) { @@ -151,7 +151,7 @@ public void onPlaceBlock(BlockPlaceEvent event) { ); } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) public void onBreakBlock(BlockBreakEvent event) { Block block = event.getBlock(); if (blocks.contains(block.getType())) { @@ -215,7 +215,7 @@ public void onBlockChange(BlockFadeEvent event) { } } - @EventHandler (ignoreCancelled = true) + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGH) public void onTrampling(EntityChangeBlockEvent event) { Block block = event.getBlock(); if (block.getType() == Material.FARMLAND && event.getTo() == Material.DIRT) { @@ -227,13 +227,13 @@ public void onTrampling(EntityChangeBlockEvent event) { } } - @EventHandler (ignoreCancelled = true) + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGH) public void onMoistureChange(MoistureChangeEvent event) { if (ConfigManager.disableMoistureMechanic()) event.setCancelled(true); } - @EventHandler (ignoreCancelled = true) + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGH) public void onPistonExtend(BlockPistonExtendEvent event) { Optional> world = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(event.getBlock().getWorld()); if (world.isEmpty()){ @@ -248,7 +248,7 @@ public void onPistonExtend(BlockPistonExtendEvent event) { } } - @EventHandler (ignoreCancelled = true) + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGH) public void onPistonRetract(BlockPistonRetractEvent event) { Optional> world = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(event.getBlock().getWorld()); if (world.isEmpty()){ @@ -294,7 +294,7 @@ public void onExplosion(BlockExplodeEvent event) { } } - @EventHandler (ignoreCancelled = true) + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGH) public void onDispenser(BlockDispenseEvent event) { Block block = event.getBlock(); if (!(block.getBlockData() instanceof org.bukkit.block.data.type.Dispenser directional)) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java index c01e5bdde..4031df8a7 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/SimpleRegistryAccess.java @@ -17,7 +17,6 @@ package net.momirealms.customcrops.api.core; -import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.block.CustomCropsBlock; import net.momirealms.customcrops.api.core.item.CustomCropsItem; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; @@ -25,11 +24,18 @@ public class SimpleRegistryAccess implements RegistryAccess { - private BukkitCustomCropsPlugin plugin; private boolean frozen; + private static SimpleRegistryAccess instance; - public SimpleRegistryAccess(BukkitCustomCropsPlugin plugin) { - this.plugin = plugin; + private SimpleRegistryAccess() { + instance = this; + } + + public static SimpleRegistryAccess getInstance() { + if (instance == null) { + instance = new SimpleRegistryAccess(); + } + return instance; } public void freeze() { 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 30e1ebdfa..af62f4c88 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 @@ -326,13 +326,19 @@ private void tickCrop(CustomCropsBlockState state, CustomCropsWorld world, Po return; } - int pointToAdd = 1; - for (GrowCondition growCondition : config.growConditions()) { - if (growCondition.isMet(context)) { - pointToAdd = growCondition.pointToAdd(); - break; + int pointToAdd = 0; + GrowCondition[] growConditions = config.growConditions(); + if (growConditions.length == 0) { + pointToAdd = 1; + } else { + for (GrowCondition growCondition : config.growConditions()) { + if (growCondition.isMet(context)) { + pointToAdd = growCondition.pointToAdd(); + break; + } } } + if (pointToAdd == 0) return; Optional optionalState = world.getBlockState(location.add(0,-1,0)); if (optionalState.isPresent()) { diff --git a/common/src/main/java/net/momirealms/customcrops/common/helper/VersionHelper.java b/common/src/main/java/net/momirealms/customcrops/common/helper/VersionHelper.java index 3bfb8b9a2..fb8acbb28 100644 --- a/common/src/main/java/net/momirealms/customcrops/common/helper/VersionHelper.java +++ b/common/src/main/java/net/momirealms/customcrops/common/helper/VersionHelper.java @@ -40,7 +40,7 @@ public class VersionHelper { URL url = new URL("https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=2625&key=version"); URLConnection conn = url.openConnection(); conn.setConnectTimeout(10000); - conn.setReadTimeout(60000); + conn.setReadTimeout(30000); InputStream inputStream = conn.getInputStream(); String newest = new BufferedReader(new InputStreamReader(inputStream)).readLine(); String current = plugin.getPluginVersion(); diff --git a/gradle.properties b/gradle.properties index 62c1f7d00..d9a65ed65 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,7 +28,7 @@ mojang_brigadier_version=1.0.18 bstats_version=3.0.2 geantyref_version=1.3.15 caffeine_version=3.1.8 -rtag_version=6290733498 +rtag_version=1.5.6 exp4j_version=0.4.8 placeholder_api_version=2.11.6 anti_grief_version=0.12 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 549af95e9..4eb5afe1b 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/BukkitCustomCropsPluginImpl.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.bukkit; +import net.momirealms.antigrieflib.AntiGriefLib; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.ConfigManager; import net.momirealms.customcrops.api.core.SimpleRegistryAccess; @@ -86,7 +87,7 @@ public BukkitCustomCropsPluginImpl(Plugin boostrap) { this.logger = new JavaPluginLogger(getBoostrap().getLogger()); this.classPathAppender = new ReflectionClassPathAppender(this); this.dependencyManager = new DependencyManagerImpl(this); - this.registryAccess = new SimpleRegistryAccess(this); + this.registryAccess = SimpleRegistryAccess.getInstance(); } @Override @@ -176,29 +177,40 @@ public void enable() { boolean downloadFromPolymart = polymart.equals("1"); boolean downloadFromBBB = buildByBit.equals("true"); - this.getScheduler().sync().runLater(() -> { - getPluginLogger().info("CustomCrops Registry has been frozen"); - ((SimpleRegistryAccess) registryAccess).freeze(); - this.reload(); - if (ConfigManager.metrics()) new Metrics((JavaPlugin) getBoostrap(), 16593); - if (ConfigManager.checkUpdate()) { - VersionHelper.UPDATE_CHECKER.apply(this).thenAccept(result -> { - String link; - if (downloadFromPolymart) { - link = "https://polymart.org/resource/2625/"; - } else if (downloadFromBBB) { - link = "https://builtbybit.com/resources/36363/"; - } else { - link = "https://github.com/Xiao-MoMi/Custom-Crops/"; - } - if (!result) { - this.getPluginLogger().info("You are using the latest version."); - } else { - this.getPluginLogger().warn("Update is available: " + link); - } - }); - } - }, 1, null); + ((SimpleRegistryAccess) registryAccess).freeze(); + this.reload(); + if (ConfigManager.metrics()) new Metrics((JavaPlugin) getBoostrap(), 16593); + if (ConfigManager.checkUpdate()) { + VersionHelper.UPDATE_CHECKER.apply(this).thenAccept(result -> { + String link; + if (downloadFromPolymart) { + link = "https://polymart.org/resource/2625/"; + } else if (downloadFromBBB) { + link = "https://builtbybit.com/resources/36363/"; + } else { + link = "https://github.com/Xiao-MoMi/Custom-Crops/"; + } + if (!result) { + this.getPluginLogger().info("You are using the latest version."); + } else { + this.getPluginLogger().warn("Update is available: " + link); + } + }); + } + // delayed init task + if (VersionHelper.isFolia()) { + Bukkit.getGlobalRegionScheduler().run(getBoostrap(), (scheduledTask) -> { + ((SimpleRegistryAccess) registryAccess).freeze(); + logger.info("Registry access has been frozen"); + ((BukkitItemManager) itemManager).setAntiGriefLib(AntiGriefLib.builder((JavaPlugin) getBoostrap()).silentLogs(true).ignoreOP(true).build()); + }); + } else { + Bukkit.getScheduler().runTask(getBoostrap(), () -> { + ((SimpleRegistryAccess) registryAccess).freeze(); + logger.info("Registry access has been frozen"); + ((BukkitItemManager) itemManager).setAntiGriefLib(AntiGriefLib.builder((JavaPlugin) getBoostrap()).silentLogs(true).ignoreOP(true).build()); + }); + } } @Override 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 995e5d901..dc58823bf 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 @@ -19,6 +19,7 @@ import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; +import net.momirealms.antigrieflib.AntiGriefLib; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.block.BreakReason; @@ -52,6 +53,7 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -68,6 +70,7 @@ public class BukkitItemManager extends AbstractItemManager { private final HashMap itemProviders = new HashMap<>(); private ItemProvider[] itemDetectArray = new ItemProvider[0]; private final BukkitItemFactory factory; + private AntiGriefLib antiGriefLib; public BukkitItemManager(BukkitCustomCropsPlugin plugin) { this.plugin = plugin; @@ -82,6 +85,10 @@ public BukkitItemManager(BukkitCustomCropsPlugin plugin) { this.factory = BukkitItemFactory.create(plugin); } + public void setAntiGriefLib(AntiGriefLib antiGriefLib) { + this.antiGriefLib = antiGriefLib; + } + @Override public void load() { this.resetItemDetectionOrder(); @@ -398,6 +405,10 @@ public void handlePlayerInteractBlock(Player player, Block block, String blockID return; } + if (antiGriefLib != null && !antiGriefLib.canInteract(player, block.getLocation())) { + return; + } + String itemID = id(itemInHand); CustomCropsWorld world = optionalWorld.get(); WrappedInteractEvent wrapped = new WrappedInteractEvent(ExistenceForm.BLOCK, player, world, block.getLocation(), blockID, itemInHand, itemID, hand, blockFace, event); @@ -412,6 +423,10 @@ public void handlePlayerInteractFurniture(Player player, Location location, Stri return; } + if (antiGriefLib != null && !antiGriefLib.canInteract(player, location)) { + return; + } + String itemID = id(itemInHand); CustomCropsWorld world = optionalWorld.get(); WrappedInteractEvent wrapped = new WrappedInteractEvent(ExistenceForm.FURNITURE, player, world, location, furnitureID, itemInHand, itemID, hand, null, event); @@ -442,6 +457,10 @@ public void handlePlayerBreak(Player player, Location location, ItemStack itemIn return; } + if (antiGriefLib != null && !antiGriefLib.canBreak(player, location)) { + return; + } + String itemID = id(itemInHand); CustomCropsWorld world = optionalWorld.get(); WrappedBreakEvent wrapped = new WrappedBreakEvent(player, null, world, location, brokenID, itemInHand, itemID, BreakReason.BREAK, event); @@ -503,6 +522,10 @@ public void handlePlayerPlace(Player player, Location location, String placedID, return; } + if (antiGriefLib != null && !antiGriefLib.canPlace(player, location)) { + return; + } + CustomCropsWorld world = optionalWorld.get(); Pos3 pos3 = Pos3.from(location); Optional optionalState = world.getBlockState(pos3); diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 7ff6f5e36..c45847b49 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -16,28 +16,4 @@ softdepend: - RealisticSeasons - AdvancedSeasons - SlimeWorldManager - - MythicMobs - - HuskClaims - - HuskTowns - - Residence - - BentoBox - - FabledSkyBlock - - CrashClaim - - GriefDefender - - GriefPrevention - - BentoBox - - IridiumSkyblock - - KingdomsX - - Landlord - - Lands - - PlotSquared - - ProtectionStones - - RedProtect - - Factions - - SuperiorSkyblock2 - - Towny - - UltimateClaims - - UltimateClans - - uSkyBlock - - WorldGuard - - XClaim \ No newline at end of file + - MythicMobs \ No newline at end of file From b8eac474308a45ecbbfece93dd49d05db79682c5 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:49:52 +0800 Subject: [PATCH 080/329] Update AbstractActionManager.java --- .../customcrops/api/action/AbstractActionManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java index 3301e7344..4a9939640 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java @@ -197,6 +197,7 @@ protected void registerBroadcastAction() { registerAction((args, chance) -> { List messages = ListUtils.toList(args); return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (Math.random() > chance) return; OfflinePlayer offlinePlayer = null; if (context.holder() instanceof Player player) { @@ -219,6 +220,7 @@ protected void registerNearbyMessage() { List messages = ListUtils.toList(section.get("message")); MathValue range = MathValue.auto(section.get("range")); return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (Math.random() > chance) return; double realRange = range.evaluate(context); OfflinePlayer owner = null; @@ -254,6 +256,7 @@ protected void registerNearbyActionBar() { String actionbar = section.getString("actionbar"); MathValue range = MathValue.auto(section.get("range")); return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (Math.random() > chance) return; OfflinePlayer owner = null; if (context.holder() instanceof Player player) { @@ -281,6 +284,7 @@ protected void registerCommandAction() { registerAction((args, chance) -> { List commands = ListUtils.toList(args); return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (Math.random() > chance) return; OfflinePlayer owner = null; if (context.holder() instanceof Player player) { @@ -297,6 +301,7 @@ protected void registerCommandAction() { registerAction((args, chance) -> { List commands = ListUtils.toList(args); return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (Math.random() > chance) return; OfflinePlayer owner = null; if (context.holder() instanceof Player player) { @@ -315,6 +320,7 @@ protected void registerCommandAction() { List cmd = ListUtils.toList(section.get("command")); MathValue range = MathValue.auto(section.get("range")); return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (Math.random() > chance) return; OfflinePlayer owner = null; if (context.holder() instanceof Player player) { @@ -492,6 +498,7 @@ protected void registerNearbyTitle() { int fadeOut = section.getInt("fade-out", 10); int range = section.getInt("range", 0); return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (Math.random() > chance) return; Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); for (Player player : location.getWorld().getPlayers()) { @@ -552,6 +559,7 @@ protected void registerParticleAction() { } return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (Math.random() > chance) return; Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); location.getWorld().spawnParticle( @@ -749,6 +757,7 @@ protected void registerHologramAction() { boolean onlyShowToOne = !section.getBoolean("visible-to-all", false); int range = section.getInt("range", 32); return context -> { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; if (context.holder() == null) return; if (Math.random() > chance) return; Player owner = null; From b6dc502138a817577a98b4cc46132588e7481ba9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:21:16 +0800 Subject: [PATCH 081/329] Added insight mode --- .../core/block/AbstractCustomCropsBlock.java | 6 + .../customcrops/api/core/block/CropBlock.java | 6 + .../api/core/block/CustomCropsBlock.java | 3 + .../api/core/block/GreenhouseBlock.java | 6 + .../customcrops/api/core/block/PotBlock.java | 6 + .../api/core/block/ScarecrowBlock.java | 6 + .../api/core/block/SprinklerBlock.java | 6 + .../api/core/world/CustomCropsWorld.java | 7 + .../api/core/world/CustomCropsWorldImpl.java | 5 + .../bukkit/command/BukkitCommandManager.java | 4 +- .../command/feature/DebugInsightCommand.java | 168 ++++++++++++++++++ .../command/feature/DebugWorldsCommand.java | 56 ++++++ .../bukkit/config/BukkitConfigManager.java | 7 + plugin/src/main/resources/commands.yml | 14 ++ 14 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java create mode 100644 plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugWorldsCommand.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java index cef84aa48..2e2420bb0 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/AbstractCustomCropsBlock.java @@ -21,6 +21,7 @@ import com.flowpowered.nbt.IntTag; import com.flowpowered.nbt.StringTag; import com.flowpowered.nbt.Tag; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; @@ -96,4 +97,9 @@ public void onBreak(WrappedBreakEvent event) { @Override public void onPlace(WrappedPlaceEvent event) { } + + @Override + public NamedTextColor insightColor() { + return NamedTextColor.WHITE; + } } 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 af62f4c88..200e8c5ca 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 @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.core.block; import com.flowpowered.nbt.IntTag; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; @@ -386,4 +387,9 @@ public void point(CustomCropsBlockState state, int point) { public CropConfig config(CustomCropsBlockState state) { return Registries.CROP.get(id(state)); } + + @Override + public NamedTextColor insightColor() { + return NamedTextColor.GREEN; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java index a7a9c25b0..92e91cd6b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CustomCropsBlock.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.core.block; import com.flowpowered.nbt.CompoundMap; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; @@ -45,4 +46,6 @@ public interface CustomCropsBlock { void onPlace(WrappedPlaceEvent event); boolean isBlockInstance(String id); + + NamedTextColor insightColor(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java index 3bb3d8bad..0b5c96af3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/GreenhouseBlock.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.core.block; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; import net.momirealms.customcrops.api.core.ConfigManager; @@ -115,4 +116,9 @@ public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) }); return state; } + + @Override + public NamedTextColor insightColor() { + return NamedTextColor.GRAY; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 0e6652dbb..c813221d8 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.core.block; import com.flowpowered.nbt.*; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; @@ -638,4 +639,9 @@ private CompoundMap fertilizerToTag(Fertilizer fertilizer) { tag.put(new StringTag("id", fertilizer.id())); return tag; } + + @Override + public NamedTextColor insightColor() { + return NamedTextColor.LIGHT_PURPLE; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java index 80aea7acb..05626984c 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/ScarecrowBlock.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.core.block; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; import net.momirealms.customcrops.api.core.ConfigManager; @@ -115,4 +116,9 @@ public CustomCropsBlockState getOrFixState(CustomCropsWorld world, Pos3 pos3) }); return state; } + + @Override + public NamedTextColor insightColor() { + return NamedTextColor.YELLOW; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java index 86c3989be..b9b580be5 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -19,6 +19,7 @@ import com.flowpowered.nbt.IntTag; import com.flowpowered.nbt.Tag; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.context.Context; @@ -359,4 +360,9 @@ public void updateBlockAppearance(Location location, SprinklerConfig config, boo FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(location, ExistenceForm.ANY); BukkitCustomCropsPlugin.getInstance().getItemManager().place(location, config.existenceForm(), hasWater ? config.threeDItemWithWater() : config.threeDItem(), rotation); } + + @Override + public NamedTextColor insightColor() { + return NamedTextColor.AQUA; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java index 18bd9806e..43fbc3c12 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/world/CustomCropsWorld.java @@ -149,6 +149,13 @@ default CustomCropsRegion restoreRegion(RegionPos pos, ConcurrentHashMap getBlockState(Pos3 location) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java index 7b7cd7d28..0dd0c389f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/BukkitCommandManager.java @@ -41,7 +41,9 @@ public class BukkitCommandManager extends AbstractCommandManager new SetSeasonCommand(this), new GetDateCommand(this), new SetDateCommand(this), - new ForceTickCommand(this) + new ForceTickCommand(this), + new DebugWorldsCommand(this), + new DebugInsightCommand(this) ); private final Index> INDEX = Index.create(CommandFeature::getFeatureID, FEATURES); diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java new file mode 100644 index 000000000..a95ad4927 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java @@ -0,0 +1,168 @@ +/* + * 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.bukkit.command.feature; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.core.world.*; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.feature.color.NamedTextColor; +import net.momirealms.sparrow.heart.feature.highlight.HighlightBlocks; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.metadata.FixedMetadataValue; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class DebugInsightCommand extends BukkitCommandFeature { + + public DebugInsightCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + BukkitCustomCropsPlugin plugin = BukkitCustomCropsPlugin.getInstance(); + Player player = context.sender(); + if (player.hasMetadata("customcrops:insight")) { + player.removeMetadata("customcrops:insight", plugin.getBoostrap()); + plugin.getSenderFactory().wrap(player).sendMessage(AdventureHelper.miniMessage("Insight mode: OFF")); + return; + } + + player.setMetadata("customcrops:insight", new FixedMetadataValue(plugin.getBoostrap(), 1)); + new InsightPlayer(player.getUniqueId()); + plugin.getSenderFactory().wrap(player).sendMessage(AdventureHelper.miniMessage("Insight mode: ON")); + plugin.getSenderFactory().wrap(player).sendMessage(AdventureHelper.miniMessage("Note: ")); + }); + } + + @Override + public String getFeatureID() { + return "debug_insight"; + } + + public static class InsightPlayer implements Runnable { + + private final SchedulerTask task; + private final UUID uuid; + private final HashMap highlightCache = new HashMap<>(); + private ChunkPos currentPos = null; + private String currentWorld = null; + + public InsightPlayer(UUID uuid) { + this.uuid = uuid; + this.task = BukkitCustomCropsPlugin.getInstance().getScheduler().asyncRepeating(this, 50, 50, TimeUnit.MILLISECONDS); + } + + @Override + public void run() { + Player player = Bukkit.getPlayer(uuid); + if (player == null || !player.isOnline() || !player.hasMetadata("customcrops:insight")) { + for (HighlightBlocks[] blocks : highlightCache.values()) { + for (HighlightBlocks block : blocks) { + block.destroy(player); + } + } + highlightCache.clear(); + task.cancel(); + return; + } + World world = player.getWorld(); + String worldName = player.getWorld().getName(); + if (!worldName.equals(currentWorld)) { + currentWorld = worldName; + for (HighlightBlocks[] blocks : highlightCache.values()) { + for (HighlightBlocks block : blocks) { + block.destroy(player); + } + } + highlightCache.clear(); + currentPos = null; + } + + Optional> optionWorld = BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(world); + if (optionWorld.isEmpty()) { + return; + } + CustomCropsWorld customCropsWorld = optionWorld.get(); + + Chunk chunk = player.getLocation().getChunk(); + ChunkPos chunkPos = ChunkPos.fromBukkitChunk(chunk); + if (!chunkPos.equals(currentPos)) { + currentPos = chunkPos; + HashSet nearbyChunks = new HashSet<>(); + for (int i = -2; i < 3; i++) { + for (int j = -2; j < 3; j++) { + nearbyChunks.add(ChunkPos.of(currentPos.x() + i, currentPos.z() + j)); + } + } + ArrayList chunksToRemove = new ArrayList<>(); + for (Map.Entry entry : highlightCache.entrySet()) { + if (!nearbyChunks.contains(entry.getKey())) { + chunksToRemove.add(entry.getKey()); + } + } + for (ChunkPos pos : chunksToRemove) { + HighlightBlocks[] blocks = highlightCache.remove(pos); + for (HighlightBlocks block : blocks) { + block.destroy(player); + } + } + for (ChunkPos pos : nearbyChunks) { + if (!highlightCache.containsKey(pos)) { + customCropsWorld.getChunk(pos).ifPresentOrElse(cropsChunk -> { + ArrayList highlightBlockList = new ArrayList<>(); + HashMap> blockMap = new HashMap<>(); + for (CustomCropsSection section : cropsChunk.sections()) { + for (Map.Entry entry : section.blockMap().entrySet()) { + net.kyori.adventure.text.format.NamedTextColor namedTextColor = entry.getValue().type().insightColor(); + Location location = LocationUtils.toSurfaceCenterLocation(entry.getKey().toPos3(pos).toLocation(world)); + List locations = blockMap.computeIfAbsent(namedTextColor, k -> new ArrayList<>()); + locations.add(location); + } + } + for (Map.Entry> entry : blockMap.entrySet()) { + highlightBlockList.add(SparrowHeart.getInstance().highlightBlocks( + player, NamedTextColor.namedColor(entry.getKey().value()), entry.getValue().toArray(new Location[0]) + )); + } + highlightCache.put(pos, highlightBlockList.toArray(new HighlightBlocks[0])); + }, () -> { + highlightCache.put(pos, new HighlightBlocks[0]); + }); + } + } + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugWorldsCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugWorldsCommand.java new file mode 100644 index 000000000..8ea00fc2a --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugWorldsCommand.java @@ -0,0 +1,56 @@ +/* + * 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.bukkit.command.feature; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature; +import net.momirealms.customcrops.common.command.CustomCropsCommandManager; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.sender.Sender; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class DebugWorldsCommand extends BukkitCommandFeature { + + public DebugWorldsCommand(CustomCropsCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .handler(context -> { + Sender sender = BukkitCustomCropsPlugin.getInstance().getSenderFactory().wrap(context.sender()); + for (World world : Bukkit.getWorlds()) { + BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(world).ifPresent(w -> { + sender.sendMessage(AdventureHelper.miniMessage("World: " + world.getName() + "")); + sender.sendMessage(AdventureHelper.miniMessage(" - Loaded chunks: " + w.loadedChunks().length)); + sender.sendMessage(AdventureHelper.miniMessage(" - Lazy chunks: " + w.lazyChunks().length)); + }); + } + }); + } + + @Override + public String getFeatureID() { + return "debug_worlds"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java index 461eadcd1..be37f372a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/BukkitConfigManager.java @@ -143,6 +143,13 @@ private void loadSettings() { } } } + + for (String id : scarecrow) { + Registries.BLOCKS.register(id, BuiltInBlockMechanics.SCARECROW.mechanic()); + } + for (String id : greenhouse) { + Registries.BLOCKS.register(id, BuiltInBlockMechanics.GREENHOUSE.mechanic()); + } } @Override diff --git a/plugin/src/main/resources/commands.yml b/plugin/src/main/resources/commands.yml index fe9c732fb..a6f2bc343 100644 --- a/plugin/src/main/resources/commands.yml +++ b/plugin/src/main/resources/commands.yml @@ -51,6 +51,20 @@ debug_data: - /customcrops debug data - /ccrops debug data +debug_worlds: + enable: true + permission: customcrops.command.debug + usage: + - /customcrops debug worlds + - /ccrops debug worlds + +debug_insight: + enable: true + permission: customcrops.command.debug + usage: + - /customcrops debug insight + - /ccrops debug insight + force_tick: enable: true permission: customcrops.command.force_tick From 017c935aaa5fbc294f2663e5dcb6bbfc9ca4df76 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:22:06 +0800 Subject: [PATCH 082/329] Update DebugInsightCommand.java --- .../customcrops/bukkit/command/feature/DebugInsightCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java index a95ad4927..25adf58b3 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java @@ -62,7 +62,7 @@ public Command.Builder assembleCommand(CommandManagerInsight mode: ON")); - plugin.getSenderFactory().wrap(player).sendMessage(AdventureHelper.miniMessage("Note: ")); + plugin.getSenderFactory().wrap(player).sendMessage(AdventureHelper.miniMessage("Note that this only shows a snapshot of the data.")); }); } From ee9fe055054fe48044bfba6a659434de362d9852 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:24:08 +0800 Subject: [PATCH 083/329] Update DebugInsightCommand.java --- .../bukkit/command/feature/DebugInsightCommand.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java index 25adf58b3..3adc88c3f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java @@ -87,7 +87,12 @@ public InsightPlayer(UUID uuid) { @Override public void run() { Player player = Bukkit.getPlayer(uuid); - if (player == null || !player.isOnline() || !player.hasMetadata("customcrops:insight")) { + if (player == null || !player.isOnline()) { + task.cancel(); + highlightCache.clear(); + return; + } + if (!player.hasMetadata("customcrops:insight")) { for (HighlightBlocks[] blocks : highlightCache.values()) { for (HighlightBlocks block : blocks) { block.destroy(player); From 5a7bb86112d6262904d51e711a98dd23056bf3ec Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 21:13:57 +0800 Subject: [PATCH 084/329] fix some bugs --- .../customcrops/api/core/block/PotBlock.java | 1 + .../command/feature/DebugInsightCommand.java | 22 ++++++++++++++++++- plugin/src/main/resources/config.yml | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index c813221d8..7b420472a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -377,6 +377,7 @@ private void tickPot(CustomCropsBlockState state, CustomCropsWorld world, Pos waterChanged = true; } hasNaturalWater = true; + break; } } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java index 3adc88c3f..0cc9229c8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/command/feature/DebugInsightCommand.java @@ -33,6 +33,10 @@ import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.metadata.FixedMetadataValue; import org.incendo.cloud.Command; import org.incendo.cloud.CommandManager; @@ -40,7 +44,7 @@ import java.util.*; import java.util.concurrent.TimeUnit; -public class DebugInsightCommand extends BukkitCommandFeature { +public class DebugInsightCommand extends BukkitCommandFeature implements Listener { public DebugInsightCommand(CustomCropsCommandManager commandManager) { super(commandManager); @@ -71,6 +75,22 @@ public String getFeatureID() { return "debug_insight"; } + @Override + public void unregisterRelatedFunctions() { + HandlerList.unregisterAll(this); + } + + @Override + public void registerRelatedFunctions() { + Bukkit.getPluginManager().registerEvents(this, BukkitCustomCropsPlugin.getInstance().getBoostrap()); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + player.removeMetadata("customcrops:insight", BukkitCustomCropsPlugin.getInstance().getBoostrap()); + } + public static class InsightPlayer implements Runnable { private final SchedulerTask task; diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 71601ee1a..7fc7d2e26 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -41,7 +41,7 @@ worlds: # 300s means that a crop would be certainly ticked once in 300s # For crops, under the same conditions, the growth rate of crops is basically the same # For sprinklers and pots, they would work periodically. - min-tick-unit: 300 + min-tick-unit: 180 # Offline tick settings # This option allows crops to grow even if the world is unloaded or the server is closed # This may lead to some issues caused by timeliness conditions for instance seasons From 9998be44118e1365e9810255d5b703bfdef7e577 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 21:15:12 +0800 Subject: [PATCH 085/329] Update PotBlock.java --- .../customcrops/api/core/block/PotBlock.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 7b420472a..1b293da72 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -368,16 +368,18 @@ private void tickPot(CustomCropsBlockState state, CustomCropsWorld world, Pos } if (!hasNaturalWater && config.isNearbyWaterAccepted()) { - for (int i = -4; i <= 4; i++) { - for (int j = -4; j <= 4; j++) { - for (int k : new int[]{0, 1}) { - BlockData block = bukkitWorld.getBlockData(location.x() + i, location.y() + j, location.z() + k); - if (block.getMaterial() == Material.WATER || (block instanceof Waterlogged waterlogged && waterlogged.isWaterlogged())) { - if (addWater(state, 1)) { - waterChanged = true; + outer: { + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + for (int k : new int[]{0, 1}) { + BlockData block = bukkitWorld.getBlockData(location.x() + i, location.y() + j, location.z() + k); + if (block.getMaterial() == Material.WATER || (block instanceof Waterlogged waterlogged && waterlogged.isWaterlogged())) { + if (addWater(state, 1)) { + waterChanged = true; + } + hasNaturalWater = true; + break outer; } - hasNaturalWater = true; - break; } } } From 8934724296277beff485d10bec96e9869591d5d3 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 21:32:08 +0800 Subject: [PATCH 086/329] added working-mode: 3 --- .../customcrops/bukkit/config/ConfigType.java | 10 ++++++ .../resources/contents/sprinklers/default.yml | 34 +++++++++++-------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java index 7cb6144ce..131fca345 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/config/ConfigType.java @@ -252,6 +252,16 @@ public class ConfigType { } } } + } else if (workingMode == 3) { + ArrayList offsets = new ArrayList<>(); + for (int i = -rangeValue; i <= rangeValue; i++) { + for (int j = -rangeValue; j <= rangeValue; j++) { + if (Math.sqrt(i * i + j * j) <= rangeValue + 0.3) { + offsets.add(new int[]{i, j}); + } + } + } + range = offsets.toArray(new int[offsets.size()][]); } else { throw new IllegalArgumentException("Unrecognized working mode: " + workingMode); } diff --git a/plugin/src/main/resources/contents/sprinklers/default.yml b/plugin/src/main/resources/contents/sprinklers/default.yml index 072c8ca5b..98e4eefea 100644 --- a/plugin/src/main/resources/contents/sprinklers/default.yml +++ b/plugin/src/main/resources/contents/sprinklers/default.yml @@ -1,12 +1,14 @@ # ID sprinkler_1: # This decides the work range - # □□□ # □■□ - # □□□ + # ■▼■ + # □■□ range: 1 + # mode of sprinkling (1=square, 2=rhombus, 3=circle) + working-mode: 2 # Maximum water storage capacity - storage: 5 + storage: 4 # Unlimited water source infinite: false # Amount of water added in a single sprinkling @@ -101,12 +103,13 @@ sprinkler_1: pitch: 1 sprinkler_2: # □□□□□ + # □■■■□ + # □■▼■□ + # □■■■□ # □□□□□ - # □□■□□ - # □□□□□ - # □□□□□ - range: 2 - storage: 5 + range: 1 + working-mode: 1 + storage: 4 water: 1 3D-item: {0}sprinkler_2 2D-item: {0}sprinkler_2_item @@ -187,14 +190,15 @@ sprinkler_2: pitch: 1 sprinkler_3: # □□□□□□□ + # □■■■■■□ + # □■■■■■□ + # □■■▼■■□ + # □■■■■■□ + # □■■■■■□ # □□□□□□□ - # □□□□□□□ - # □□□■□□□ - # □□□□□□□ - # □□□□□□□ - # □□□□□□□ - range: 3 - storage: 3 + range: 2 + working-mode: 1 + storage: 4 water: 1 3D-item: {0}sprinkler_3 2D-item: {0}sprinkler_3_item From 14c3d0a338312ddca2e02f3ded16c6680a48c94f Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Wed, 4 Sep 2024 21:55:11 +0800 Subject: [PATCH 087/329] implement quests --- .../customcrops/api/event/CropBreakEvent.java | 2 +- compatibility/libs/ClueScrolls-4.8.7-api.jar | Bin 0 -> 714759 bytes compatibility/libs/ClueScrolls-api.jar | Bin 1276918 -> 0 bytes .../integration/quest/BattlePassQuest.java | 84 ++++++++ .../integration/quest/BetonQuestQuest.java | 195 ++++++++++++++++++ .../integration/quest/ClueScrollsQuest.java | 61 ++++++ .../bukkit/BukkitCustomCropsPluginImpl.java | 2 +- .../bukkit/config/BukkitConfigManager.java | 5 + .../integration/BukkitIntegrationManager.java | 14 ++ .../bukkit/item/BukkitItemManager.java | 5 + 10 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 compatibility/libs/ClueScrolls-4.8.7-api.jar delete mode 100644 compatibility/libs/ClueScrolls-api.jar create mode 100644 compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/quest/BattlePassQuest.java create mode 100644 compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/quest/BetonQuestQuest.java create mode 100644 compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/quest/ClueScrollsQuest.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java index 5ef511714..088347b98 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java @@ -151,7 +151,7 @@ public Entity getEntityBreaker() { * * @return the block state before breakage, or null if not applicable. */ - @Nullable + @NotNull public CustomCropsBlockState getBlockState() { return blockState; } diff --git a/compatibility/libs/ClueScrolls-4.8.7-api.jar b/compatibility/libs/ClueScrolls-4.8.7-api.jar new file mode 100644 index 0000000000000000000000000000000000000000..783c6ffd738bd5fa0cdc545cdc657cfc9e17317a GIT binary patch literal 714759 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
  • )2Z^RKCpIoHzKwHqlKE&WG~2Upt3Ia7up~{3 z?vK@_Z{|U}O_(e1Fa6omp8xWm=KO0+(I_4s_;g8$$+#B!8VIP61_!-X(HNh<6ltk}4sB6jTiaYJnT0$8S2 z)z(Eb*oWYYTMF4*!~d4b&lEtgUPIrvp!Dld$&&KhTrXzLBk`e019yHG^3Af~F5K327Di}UQM!;?ZoJ447>YOBfCg?bAy%Z;$iMN z#*;U0yDuYe(+ETJq#(FWry9WUX+b_U`c(dQft&3L32S=6G5h^qmawD1PJ7q z;a)HH!1A}5b+?lp1^0&*)VZdVVAE4g;AzQcVx1r$>~jr_YrFP2h$Ee~i%c7^3>(Y) z))QsS+@vWtz0=;YvtjTD4&<~0N9znVKkA2H;MCKoEb`F$;Wno#?4>*mxGV1)&dl$r z0+6)N1z;Z$$WEe;rL%u9wlFnKtt5)_6^Rt_?<7=Ff*5Ig2z*9{c&B$1Egfvxsu&wGc7u{Fr)4?r?VvZZJhIA+uc)@vp zo8mIX)RbLp2x_$>41!D^c1*g?JookCngEP7*OsXJ_Q=WZEU9u!&e>u^^dPsuNU=pF zY8S3c5k`G&cq~;BC#RSh!ks|Av?%VR=?n41!MYJ7a~`NVh0Z3irr!VT_}V;=I9#9V z8Jgt#QR9zGHRpvHK>zGc9LL}#nh$ICQ*=Pur!epKLBiSWN-258&F+st$}HI5(0VZO zl-|KH214j?$L7I`pU~h1M&MiS8Kvjd5AOT28^$WoMd#{_%5F$@_cy8bpP>WTSv0*U ztNBQ0;jKUvPSaR+-QCwSxcx68&dTTlG)mmNjrTGXF=2uUN%K8AT1%SI*yBz0e%&>N|~k z3WiAv%y+{|NtV=9f7b9~E@eBm<<^EX(|fYp$`6Siu+k|N{+G0D3tsk)kYI*+tgMW) zRo)}cX^*y}jX1xLCk+2nccE4r53LgCBDCk98?SwUM)ItZ0}-Y=?`a1RO`xn+UiLV8i<^jQ1TL(4ldCyQeeg^2iWlMdHM(0n^bOWr6J7QD9+ zM2O~O{(KJ3@2|y_Jolo7L~o*j^=Us6_eR?7uT#l_wto_&S_i`|#-EF|2~up#ppUHt zaLt8KaS=nQcGm5S+E#BioSL$(Z8Qlt&Y)vE2KTnZQO_NTK$B=|LQ<*Hn=(ct z;SDDy27 zn!=-i#b~Gwpew_m3veF%M#6<-!%&)?ccOdYHS)?B_!(+$dA=Am;H^}gE zhCeaY9H;lvu+xQC2x?VRX>c*bRkoJQI0etXEeRQOv$Do4yG&F*Nmb6;TLn`?lFb)N zy>gDUwB}QE@6Bh(LePNX*UKCa&d_wLy`@9Sf4|?j*;~?ijAQu7J;vK#v_ORl({96y z%U_&-c8|jJ#dtvNyyZt{r4;Iq3y$(4!XG<|oUk1OG+%55eS_TR*% z#U_&ic0BWuL}EgZ8m()WZIdJ5sJy}J-9?V+Jy|cVW*W}NQp_u<-dIKDI3~z6vP@FNXWHH4f)#Trl2wj}6ZW#S?j3j9wOWYV9L-n9~u9-JgO`pp56hPhvzmS=4) z??YdK#54#+(cY(kSiOOgs~@)T<&F})kjOO&u*)PKN_WDYX6U7TtlsPhnEq?K&R_Pp z1b>=qmY*&AKWSGrM+1BNf3#~#;;39d1FC3k28;=vNwU7Ini$lIGq6b_6~JEwdWqZv zOxZ{+7Uj6MejD_WLd4KqkhX#lR*%#7_x^KtXID=r$Vxv%zkxnJCnZcaYsU*DH&0PA zBvn2~omhD~Y>pHfSz@WNkp)Z7`vMO2noR>ZLud$O0!&SC?Bl_?@DP=LazGAqGO;7Z z?U`J(tB_^GU3`EYdvwgB&77s=uq5{h*Q-zU^Z6 zgTV-tB&A<#4m2%eO?c+Kc=E@A*G5rz=m@QzGO2df7}AoMqD(I#Mf?4yP_@|MKI!b| zG3$K>t5yD)sr>iz{oHskeBO&l|CSy9k3GoW-S;FND-=Q0(5<9NaTFsQek-d=fU?B; zHfh06kq}K92q8);T2E!REJa9mW)oLeGTp~0FiHw=a6f;3DIOfdd|ej!t`(=lJ)fy= zX0Q8)Zv;I+8^cTn)C@jE!dM(u7rgE(h`br$gwZCb>L{TJmo5c;ZuQ6!O$khzXPSO~ zdTWQejr9)&8anb1?9r~>b93kd!R@JXw;tH)AJr%cO%svEBH<)gh_pXQX2^+t|Ew%q zBdl_pIxZg^#HXy@KnX0*Y12H~-{YB=YwNvf{e8MLCWqyL8aa|DqRz>xE|8(1!j-Uw z#Z4H_NWY2j7SMF=Cr}w}F@frUk0x|cSZ@$l$5S$7OJ#ITOjo>b(ziDHV}1sVpjoyE zcS{La?J3jh+?AX67PTgYA&Rqac`bNJ{=hN%IK5Gle+9KBEq)NBC11Xkd+LMn6<&B# zG9ORKwf~mXprn65b>p|x%h>PGP9;wZL!1#=G$|Sf9Fr&UVkxq7Dtr&ZX03hqEVDR| zGY4I5otIdr$~(K(AJZw{(tXZ7ei9v?hN<&Nz-}W9pX9F)g<--qS{5W{c4ITRV9CTDc9xbgIq~*(VRX^O{B*R%{|;i0&(EYSzp4?F#dE-UKN1op zteJ_?l$>#7J610{zvx`4pPtc~kJh?}-CRt|=}9-7vjf>L{N*qVjUhC4*i&#>W^>HE zkvc9yTJYv0UzcXO8v?u<&N|Uxnzf&)vjr=YWOk52$Xt0ABqg7^zYFVqAZk6e6bIc6 z*#d$#u_-6IIZ;oCl_y3kccXcOsaO_6mbm90T^Z z3AV+uxpgqcrRP<{vg-#ZyO90$p1w3p+7)Xls!v;Jg{|AOyN1$Aa=$3^g?mro%t*K+ z2UPefwF-AzWAn5IaX7?k+BT%7Ps#xolZUO}jnyr@VhqY++afAZv@UV`$0WXRIXY(b ziKx0KGY!oBsA>ZWL^zSnwk$NyHEo>%qP9VYYkZ=bN4sTeQqxy6;R~}5CDQyx|1R-R z(sud_tA=pQp5u7m{U^gbG0`&sE?nk!xjcvMS~)1f!d6g zcU(Ac?2Cu123<6c&>+wdCNXRJ??noSRpwnP!&b`3PBA&waDWi*O6$dSYtG~0C#jlP*9k;nx_TE8Pmi5LQR{_A91zCx|+$1&Wkr#7@6O;nYoIe zl9De46@e`|Cxc(!%ZjxwtlHXIkGM984G=p?iJ$D=(Cv4R2BcImV{!3%X;+kMv%no$ zVWL(ykE|pwQk5x&`Z#N(wV}hyNv>g7fFV03gK>J(R1vYuv8JjCXe`TBT&%QFWy!Ou zh(*GvD<=%2IIiznrR-s%*yQXCO(9o^h20npFx*7ORpVPo0f>Ev^u-fvN7l+-!0B%h z`P8mZr!mE_`AEkU6Gy*44Lv31$4r??b?Xgy8K%is)?R*(JbDb}QrwV35%x;VV}3e= zal1IC4pi2uyOE2O$0je8?vB8ZaweFKVwYj=!nb&h^~Ed9nBBmpHuz0{+k43y+(#!3%Z@IH1)C2ScNcNaq1|vEeL38S1z)(($*J{ zhoG5C)P6IG*q;93i}N)YK9HQDumpZ-|@p@z6rHiY#{XVR-ZMN-Wfbj=@C-r$#p4{ zwY}FIk-VoxwT-RkRJ~|f9G3gz4%w7J`cV~U{y9TC7BR=3?E*6EM+|a9`VreL^n=(F ze?X5(88cmz@Q7`C4XI2^#!93Yo`v2E-XwLg8hLZThKinVN;3Jz{<}Kf}YABag83N&>?jLR$Y0+)@a`FkHh4lAXn@R#qC; zVa|Teq&Ls1X(tFiC+&grd|S4~qR5~RV?7R4$gH-WzbZR&9KzYZNZJ3NrFrrHQOe%` zZ)N|tvj1Dz|E=u*7b&|Y%?T(a2oMnW=cVg^cxPw&@07hd+3L?b`^n+lK>Cnp`C^5Q zEGs*=q+*XquqGT`uu_Hkt@Nt3Sch|?vMD{8x!@&agg!LIBj~H*ZU$xyxCQpw7iMm= zL!Y0VPNpja{5`&q_5`e7Pu+t~0W&IdC*H<`u`%g_Zly=9=fOsNt!<4XH(;oP z5GG!6c=b*(`0g>5{mb}%9{)KnzSn5fLo0R=vM z_gyJ3H{oYF0f5vlOiG)6W(nUdX-x@Z(C7cBA&koWim4$uAMDv!oj-y`14$tgScBO7r`Sn;l>Li z9at~|X+nb>@*2x>IXmHUgso+pPl}XfL6-XEq{}dRDieI^giWGC_h2UGiHvSh4BE1N zuUH}j6e*P#=#>}p>IJt3m7Wlfq*CZx1RZauUBJPcEWz#&>=d2w@OkX;>NWvbH`++A@74YpF7s#6&DdcZs_~JVQ zy*@?^KazZky4N1UKS2IU41L;IfPWD&{85_!C+-4B{!I-3CWe0#!@r5){~}_@R@q;p z_}35v^M6YWDt{ve-ucPG2*#K@#Y(_>re#A!&cJe7xI$EZfPxfSlrLr0qZQ^xDKbO46kXt`Z^V~@b%u#9&WEGPHIJ$`}9&FH{#r>aqR zj}QhJ!=Okoqsq(35ckJBc1pMhsOUiO?ask3|AJY3lr1xfA5?)cOE3!mEq&08l`3X# zZGp*H>Y&R^&yXDZmB*pDtAohrD`8q;F0(XvyU`l&(M~LMzJn9e{S2P1N*6ns7Py8xNvgo59__W5 zgt3TD;dTB#Ttu~yndFQ3F+Alj znJ!+zquyZe0}iG1NZwKY3Jh4r$9^K89J}&AHxK@(%>MueHu!dB@Q>{Wo>2Qq(|IC31X=j$24!PS&;R;Z2K z+}Qu_9$M@DD`_EzAO<;E-i4ANSUV+M}5&=Lot{DJ(FirRw=PBIMkk z|MVW;9)*cUr$a0(ujv^ZCg9>jtaOx)lc=5zpK3_F%g(b^iBgUWW{8n&w@Dk(D`$lu|C-O_+^{#v4FuavHbwmMVW}oyA#jD=A3$P|9Qbn@CmAyS=2J~wMLV9}lL_$2F||Q* z1>U(vbsC;z`CD}V&Lk^wcJo9SNq0A~((HN$)ypFEE6ebQb z3%?>Pi4jF)cM2%0NI3D#acXxFU_wC4nRm>jcqSEzD8aTc8uerNM)RZN9mmiB?GJks zCul(F`V*sJx^Hib#y6H?awL9}I+9TgW1FS7zz095Z{_ZR(^z7e-P}}vmtU%mj8p*e===zCuXH zbW>03xktP=E&=AkHeS`Cd%feJ9WU&b(Lp-w{*|35m_*e~ZbMKL=d`+qQvXL?><5D$ zdf(^)t%}{Ay=|@zDt3eftT2m%cwF#(x{O7!8*u0+c%RDSZ@WK7U;slSodW**R>F#Fuo=evO(1;17Dh%}n9iK2?O8>v1$%=>ROd zTcV$i)T#bP5TewufGs&h)iY=5Ci$xi=3~KQ(a0T&Y8v8m;X+?jH;IMqt!)?mJw!F0 zejj720?HtMf$*PZ>M%kziY&GBkrCPeWh>Dn{{e`Dj4p!UO;TB>rrjTR2I3az-0R>~ z!3W5E@0|8Cuq?iT!+d!J=StI40G^CE$(FH4;&2U8rRR@rBZL z{NkZf{R8=b5Rml`tGfuV8RWg7pj5r!FH^U{ z*NDZq{*lmk12TS3vh0P@t~y|{4YReDcz;2cws`()^L#SpwSrF%rMp4r+c+tv@aoan zFQsFNm+uWAbP-mf!l8GNJb8cY9JqvqTs8>x4AgCMnM$!Ec%6OJcnuLllq*@|=#C<4 zwHLjxU2S?)y%PjMMp4P*O)2oR(nUgv4xW)7QsQ|btR{+aayR!HuCf%y38TtUr-+9U zw@~J+m}z2WSLPTD<@P#Fb@eImWZlo8&CG5=qGnz{oBq# zE{vNyYr~bxb$su?Aqf09j;;@ZSr3vRt=8(+IhN zqJx(1+usNxcy7mKdGG&?Ah!QP5YL|kvG++3`kw?5AbN8vph)y5LGXSOMA+X6!sicy z7??z`{gWVk{ve3`zYzoy4x=lWg{EY5Q$ zkqwZ24R|&&Wp?uGc}e&BE%W~L5Z42gJs1b4Hs>n0M=tLb%j<0Gt}uRC@cCo#hAelp zMSG?#8a%TWLld!^c7JnIM{Mr2)6s3vyczxmzS0lOc7k7`I?OKAm9mrkq;%lbA7Na8 z3?F!yHLMkm{^s#e6Zcx?Cb5EY2BBkxS%{SUuj5^k?<4;RAYlD1fKbJzr>@ELX8^%` zzNEV^C*d=IknkBm*lhf2H_0HwA|RH8{?LR=DarjD_-6p2fB@pWJ^4PQe_$3rH=2e0* zyLR=D00Q720fg97i_ZYUI9w@lR^)&PQK{DdV(%@V>J0O&-2{SfTsIor65J&OcXti$ z?oP0c1b6o!!QFM^?hxGFU6XgGyJx1mtLM~ttEWz#5A#PnPhEAbwSG7GR?}UsdLKR4 zXGj5ISmyYU@=SKO*=3=M<&Fic5AB4>PR+5+4N6QTcHZCR2o5W|pfL&k9;P|-_8rl5 zDc7{%6yLCY1%ler2fRW=_Ck8D@k!NS-MB}>9t4oNQO85x+3HqBsabOX4R|dl-UtRf z3R!WgGrwnf)iCN|o{?j0MwC-ziIywGkOXbZZjq8=1qo(A5Q6lt2m$H9jCf}t8R6}# zW`JRSC@R+AB!00oW@y=*^!AfjrHp`2W{A%%`oKRRoYV3NSmHE_ELnjVrwEEMQ|F+C z1kj`u1Ox1(UYUOoqWwP+g7m)-g8A*4XBL`RJs+3px@yA(AsVrZ4__XFam^UxyTv_#K z!U}#{(Bz!Ig`ESxYF9fjz|k<52rNP&O$z zfc7pngdvGt#`#h~JW$*OgI8h8kbC6JD+#z00>~J96c)8d-ZM6DbaiNjWDn zd7s)gRLo8S;`g>eLx-}HLUz(E69@i;g{j2pY`)^1D3hb+w$X{zueeY84_W;-j@9Y? zOguh{g40q?;$@u?nUp85wC_8T`+XMAzmZU1CjEf)5t_y!eFS0SegiFTiof;|ro0t} ze8JVKH)D5qB5LUAx_w>NKx)6fR1Ais3$2MnjD9IRyH`vb6N+JpBzrGi(P#=D!C!p` zbkSQ-PZnmdZr-Yab&MH5>nrHkm$my(oIYR+hZKXeaub_>*il0Y39eHAfe@78oABkM zn4ZHK zG)m3@%DL=)78N0V1lPCp2F*dyekg=Dgpt`B)u;!;t+Ta8*z%@E)8#B3@P>O~3BLp$ zQotQ?XtW+$J&l4E@%<#`w*FWD)c<~NaJa96lu3mU{_WJ_&$9AgB1FBat}=lto-YcL zwWiW%ZL89Gc`Z(9eFP}qciN;$HEGlE@Y9x;#!dijj@HpLNgf^Zxuam$Le}vI);5R2 zU`z3$gJb9W=K{YG^|44;M_UZ$a60Twu%aR&QI|~= z8+ALW3^@^f1R6XP?q8(FYLCgn+nK78$vQe1@=l~&olO{)1xh;h!Ji!&(PB~hV-SMs zN^g$DUN9{N$jH95u3c2Qb%m9pMEc9JIuENdo{^ZQd?2hTB!o%7fRfKL(%?iTDn}E; zZdK9NzApF(bvT6FdT@=5zKgivk-7W{mF})BqAe#*nz3*ap&iMF32&T)r9KKPuv=HX zw|A7u!Rhq3HT7x1WS}0j_|yPX4O}vzu~DZ}6!L!CQv;FUz&1b^o7Bu*;SL5IlghxS z1;CAa9|@(z6-5mo1S7zfx(E8J)Me+a!qC$e9cnE@!4Dm=+Vt-tlJRVOEUea|I>? zVTn&Uc0E@7yy< zS1Io3l9Op@D&dLP8lGI?hIgl3TDE!CjG4wdQPWC;Unj*3_K{^aJ}Oy$*a@Ikl{6ZQ z8dP+VxzyMJA9>=TF-lg^?jT!k+gOI@nHSc`Zs#3&gBUUxj{>OtWXJ{^h7FhzEwChA zcGtRF!waBv7od5LQ@+P>3Ad((Hig~;bovQ_(w2L5Y>uXus&Z1=#t%2|QGN$6X6l}n zyzsLryLvhgBg~)>Z6Gt!gm5PKPwDIRV~Z}R9`TSQ9rAf11!b43N|R|VCS@^`%#*3h zcy;73&x#GatLpf48zRz*q9Efu^@QHOT9EnVJB8mAVr%u@i6q*Dcx#a=tL@uNYW4*N zSUBTn(Bqr=nnDY0ytZid1}0mJ`wP(R8_bcwB}T5s;2~L^2gUC_>{=5_`WviLZt>GT zQF`9~m=74jH(7ljBev-=rWS&zXxYPo1%^QWXf18~veu>o8$(}CV^lV+&RowcYZ@lw;p8mZ7w=5ng@3B+_8EP(N{nU*^bBCWTBB|l!W!>jLPrV*b z9s1(;`ws3a@k_5gslf+2zCO$b3+HS;Y^6B9QVJ!YEu{>3Vhxt+gk{Okh(hCL3ky(% z4(FV6TDtCxF8_>?E(V@3xGhaJG2zDq9XacD5M}OF=V$pQyNSRc8-R68?k9|F;xJ?x z)F>SS0usDX#d9q1ol{!H&wU`>t3LvX@kxv#3<+2coS|yUf`pV-e#6U%;3DFfw0u^y z4>D*JK>Mf=ycDM9rn7kESulc;?pj&`_mD;L;I+ji1~Doq-yfrLr7U! zDjRRm%dN)&du3cfd?!5gagx^E?Xy8>`vwYk%Ti?!18^0}eLdp`wK89x8LchY8%+*~ zLxM(P@{Xz7zQuf2GJav)EDVW|@&kx)P>`L}TY{@^@^C$Y4aD;8#~S^e8tV8US7dF1 z)wkmzMfU2h(^$5JN-NLlrUA62ueQdgIGE^c1(^m~xP}J2%gidEdGj!sk~{W{N>UG9 zeJ;)Z`OOb#Vl1MwlsC)|oE&3C<8f$(?F+c+p&}OP2?p9s_to9 zm6};i2LGM2`%2x8^y3pi5tLr615o6&L7>LN(Zp2jUB2(!mVpto%|U|pgr>##ZEG!tl6NguaG=x=u~gKbbAy*Z;BQjI zU~y#}U1$w!SAgpxs5uU`1NIUkm9q1mI3E`bdAThWrNFB1&J*+=&1y%#iWb(oJ4 z9NERZxK~9pt`2cNkN^ryMz5}jXCO75FF zGh$Q3A4lKQFUCk(Jw9TD8t^ycb54t9Xpiuai`#7z0qr(*5w-X4uMX`|*g@aYXU3#f zzqE>ds@pJexP16*UFx(o`Wi}l8r`u`(&~>t&l??dE#KSxLM!a{vvw}nX|~!C^(iA4 zgyTNQJ>7@i+IPHw`=HcM?k!rcC`)nhh6^7t7|-@K8;P_+S=E4C()&1Hge^%kV{@;G zhn%c9a#;l-WUlW1<3MCY>PDbe_%_pSDWWrf;g|fTiRsQT9?Kb`jI8x3bvU{bm=gd) zF2@65EB^hC{RS}2FINt9en=aKGK-IWr&(SR)^ z3@T+LH+~B>7h8AMi-?o23#~hBY89rdWGGPD-3dEDdLQ7r`QdhDSp0pQ!X|~Fh8G&^ zgniNM4cY7NVSbk8s7K-3Bitw}B6_WnN0(91hH#K4PR!Nl-IRPvb&m?CnfhTfJb$#_ zVO}G;ebuUDt!B7y;3Rd)K)`7#QEnsNPtsAxrfjg6ywuA|;l6O&IZ&scV_?ESogi}L zAjKLJ-ACe+fNxF2*^oW~yMR}mI>Jyx3C%|*3S?wiEW_Z3keyB9t56lpSaEJ|-C?q2 zg-E-La?aL1%xTvFr;;?Q!^iw>y?;KCZ8z$wSP7v`iGQCq+5damv{7DB!SZEztJH&2 z>{G33RX?4EjAT%~F=D8#AsuE*3l&gT@6PF^t5kzP<%XMD%@Z~LVD_>lgESWzuvE-5+KPB3NDq`ivspE~+9wzn@%8%QU#j_)LWO@94cJBZ?r!gtt&Xn2UVi zoQ@I|X>cvKHLwfBR3^fQPS-b29?@Ij9b{7DdS67yO>he(uV<{mj(P$?O_Np)UCqnC zpr(u0(*7~oF!tF6exna@!l@?ELy$W;OFoy#6HZH?Nry+@oQM_{qUA#+Qw68@?nY$( zpk}ZwbjTE#sSYU_l9IRS4zxJv`7B@MY-59>YGUH~XdVL$x@2&()&c4m)Gko8gkT3~ zafg6z%Qd+HNyK9qc0(+&V}NU5j<)GICUp2SzTSBkZCkhf)$g6DOR7KTKu9BAFu=^i zHnqNi(h9JfZgKkQ3d-kbZ=XJq?N^ z1?3%{)IL1}8Yq`xpv&=04;+(&TUp4PT1UVA*!>xawKf1}3FSQ)W4qy%I%2hxkQ|JH zug%X3z3X|2lIcK1!kV%-6S!w;z*r}D66dd|Qz#c1oN--{$F@=|61BxVZWN13Uuy8V zg<`xvmXjt@o`&`+FV)fxN?mm?-g(1Y5tHrF$TGmX7GF-BG?J_&YNhBwCQKz~yG7oJ zNLeK0%E+8Oo+GP&AGX2>l!7xiYhigHqeh?MX_FOkAnxuaAjX)to0*BCLfB$nV{LYn zfYDAwx1ENY(8FHeZO*seB}=*VahQ)JshDmRrbk|tUKD-IhCPRwK#g2o#=RrQU{Eo0#!5}q{dAqEgz;yhR@7z&qv|K6U^&4@PXnSYq$~ExeD2aQ>Ci=Kdmp8nX z;!K_FjX;-II$m|%^TVKL=v1HYDWlFx?;>o)ONEd@*!=Yl-a}H4C>DblCxu*2_*>73 zJP>-BBE65exn(g=sxR5df$!o{kkKlb-3Gdrm!UOh>WTld{Tr5V&MLRJA}7D7`ZuAo zu4T+G0_fhvNUi$i69KP6$H^LHV7VFH(+2dgO5bJZ*TnhCczc_FZYsUu^m+FH8NEIJ z`@!R%AH-j^Vf}x0n;AZwBhp+I?<)3`62yonY7x^)u<^Y^G^}@)1XWd?^smZl%?b>m z&gXB$<_}E#QY1c?NkYSs!#KHwW=&7^^1F8&&R+lhjwy-+&vfgqx?h&~p3uwM zkfn^$UF=)B#aTbmGMlb!BMT^1hCmC%TVbHR)R14Kv;tfu%3RbXQSHLRN4<@xNNq`+ zRl7$)kQRLO^Isbt?|^|x1X#yzjvGM+OVohfT>o+{m0T@R7zo->+|M`LR$MIT^)~RW zRyASDGC6|`8{%j0rp3iUMGhmZZJ0J=v+_->{Aeb{Hz$d^$e`~+j?CY@mY4) zbk5M^KW_6@e3Y>G(u8wsuAm4K19eZ|Vy&2%H(VWxpVaL-i1QGXi?V8|v<0exT6a#^ z#p|I^dIL0LD=OPedc5{~eWQW`?tw>PTEyjA&hYc62gc^SljT7}`D&oSpW~9fb-Eqz zeX)k7alk5T6%pTmXChJJa%qWJOAd=R_uI0Q=9Ra#RjkjA(rk<@6)+Uz!}?`(BtQ8G4$LiMY@BSwUCjG0MMd!D9(1N zkeG4@u|G`v!1Ez%U7_RZLjFfe7Z!L#q0ZS+lp;T1s_pD5)F-3n<8Zm%2{|_TL&}ds zS4riI6v6fnme^#!b+!iR!#2sg5Z#pS%sMsMtsq$hqDNVtf z!IVOoMW*e#3^&>)VfFvQu%TZ}upu{Bt~;2Ybg@p&XW);8AGAwpz-85m{&LY?sM%!1 zfwrR>v1#ann|R8)TxEHQomJ`%ssyI-dFiDM)!TwD3E+`;SXmg1>oexrgo$)fHOoE* zD&KyB$5w`HjV6*OVbb|QXIoc@G>gc_mNRm@4rdIO5r9+jl7Q{6kaVzAhi6B1fJs|x zRp;jQnAuPw*wD`79B1%F)-9T=v#=>J8rUDcmaoaBiGYiWnVy>#&`eQj=a9K}#eegW z5SzAX7Vg$PBhAN-i_p~l(hpXMFU3-k8gkIw6dRpaaFoDM#DmhOZ6H(DNB$wtcF}@* z92Yr$`_qux*R_Ld;n!)GvZ3hUQ|&{CEkssbT1w zXF7DpynsF8k}D+8ICIU+CF>`?f!=Y6N0MSLa6YR{w&58@nV#bj`N-=H0CDPw@2$yO z@H=YPbjG*1t~}#a&M>6X;`u0qg^A|lf#}oNys}EmAsWB1zr0f|nhX`MtBFGX;_`Jq zc$23@^wQHca0&3;FiEYU>a?_eDqF#*yY1uD1Es6u!LH^FDHJhbQu8xaXou8+RoxX- zYljxAvaVD8B;&#zjBEcOraqeI#i=sWkQ{F&f<3U8a6AZFfq*!&*9wBT^iOe1( zDS}8<3G!`ew?}M8@pp$VRcV&yFIjsmH2?f--Lja!Pld1-!N1R59REFgokL6?7Ju{` z=_IVlUsbB5Y8I;>{KczsY$g$_6X&pK-%HG~Fs7Sn{5;t@la8@Cj>u>I#p?UV@|p3x zLP{R)>lkl&A)40rj%O?S@@13~d&j^e$v2XihkP5cgJzdBz zBe$v}29r6{h`lGc9N1$F^%wA3=F)IcqgZ4TqXtVp>&NSDl%V2ql#%Br_7o3~+}^s# zI{<0qZ7#XLSPvB|JgKq0yBAg1MWVY3uq16w5af(tA~+st*)8Df%soGF^}fx6sW+z|(aY<5kyuH{%4)y(^G> zABf3ivBf3FymC1G5_Y@2;XwN_p<%j;Y&ZgMMifipy;SMT|DcOH;y}1s%8$olKrTMD++HWiiH~P+fEK<7}Hqd zo8pdFH*>#2Lx!;0@0e~^lUDOQ%x!?rS(@|0{&)KY5zR)v*3|A+cX`N7Aiz(YMJpj) zY6ZnaSaa*lx~xx1o);Zql9PPLVc)fG5~A4Fq&&pI^4~OYz)NCy3M~kWAt@|jt85OG z2?^gPKJ}<(qD5}jF%rbVddk#~HPVmiU`+=pK2eQzO*Q)oHD1Jw!%F=Dt%JI_vy477 zstTj1l%!iG3#+8jS*rjo^RSF};8if!D@o?b31`yYq&Tz@qjFK)!Kng}vpomH_6S-` z`5vjve9Bg7udhl-^^h_4)f#ejZEDN*ODd89zvC|ZL^7KZPqD@u0=&uJ9CgP~24u3w z>Nl?Gu1{W5#4XW!UNrY_7F=(gHG1YQ@C z-5P0yTWmL~Gf#AI;W6;Z#eBr)`t04YILnvbeYjDcY|}f| za%m0Qw9M~CpH9gzzWjO$I%KR{lj?DKMgB*nH$N*?djg@Xt$&}g{`?C5gR(5t$wjf+ z`{pN4$a4nwn3#fWc;1iDY*!h7&ND#ALrXSDD}YCqbvrpThP|bJ6y!%F<{sEK^Gg^| zPoxThjo%;?n4Bx|yzkIHZ}(MwxxGL4dy~?252$r`7TKnXhQzX;xUuIk`11S4*xa_G zY|<(UwSi!nLgbHNqJ^rDHUm@2v}X~gk+P>Y5w!XMS#eJ>$ShpS(&|~f_j}Gur-GfC z_=~cXTVuRVcwhT;PH&nYTcBs`0z$b2Db?z0NbgIk zGxea+0mi;hs9uDAig}znHIJ}t4qc+hI|LY&!Q%G=VZY^|xYz~%g|evdw1gXj<-HG; z>8O~;E?BTW>Pn)I6x(;^wDK8d7g&G8IOKi)+(+}NV7gfwgvDo|P-zKj_)>OP1DZZn z9{uqE?}QahkNB9VGq3BG+k&ae;S%y=&`9kfB9{zn%+^=3kL*(+>iq5+-zh!V3Eih3 z^`=hp*;2}Q-I0FwtCXyCg`#hOyePY}1N+g!tHOqq33--k8A6uJQ3NODdNZpM9-P!o zxxVyWXpa0;^s*3Vvq@CTtq7TZr^S-qs6D@L$vte1y_Yq`@G&8I-w^|y*Y58AElh{X zB_ZU5sMlS9bNulP0J9Mue^lLAXUUR#79h-@38PtI^M|ki*w6^P!~4<)LSvtV?Hs&d zuujAr>rINT*zR#$#G+7QRJt7`mj6mv&HYU35W;dTN&hQhxer1JtJ6hFnXri(uq}!@ z_93$@Xhb`7OhLF?C&{dyi}&39@ewvE982dY(34Ltvz?I;g|+QL0GiZR+@%;dnSri0 zJT&wDZX|PDTiU0e`{PL+jCXmqOAx+F20-}gr`lJB1m|a)oLJkq8@}&fblF(&=|jd9 zpE28+yrn1TxHYAju6EK9<5=*mK%j{!Z&0HjdKFOKdGp0WaaRa8?5r>~1n+{hmQ7jEC_3mRU=u0g%=K5qJtI>yuV>W2ehN4E z1zRz2c1C`e<+BQDn(pUbY9!1=4Vz{%?Uhr$fh%@U8CGPAU5aTQ_icU^)2+DMuy$+)yR_T6Ymnki>6I*R05y`xqA5?z;w|3$F{^D45vf0m& zFn4p2z0>3O&-m4qGowf+k5zDN;fY#rd4*p9{n2j)ga*;0oW4pO7TrM+wF-lG1`(m| zLSuia50;{A=BDu#jBmh{M;2NZ#9o%Sz8)202znBg6=-?-iW>nF{`p$VNQVj0{~THU z>l#+Qs?8s=f>^Dj541TDt949o^Cv^a@kY=hZKz2jg9@IWXW>k@`a@Q$>F@Jh`lo=u zWjdYzkX4579mHz2igd}@^e71IoUOM0ky;bn_d64vzQ3WnM9Wx4Jr&2${Vx(C`unNH zGQ-We^b~Nbsq^255)pH}hf2WiI_-|9dh}Hn4j<1XdN$SaOUhzZC*#9ZeO2pr;< z?=2Xmv2c3+4M8i@|3|c%1)$RV>;Da{cK-!hy;ccEkTv&h)crxLU;i4dVgYmjYMMsq zh9O$gc7IMU2wLU+E40ewbU?wy17)3Z5Q(fk>0mvFFLi%TstDO_sW_V>2BcQS<{zBu zEfo+TM=&|t_g&P^p&SY!<@+gqsGHtIU@7RMxoWp(f}qvPKcdxI+Qs}n%>BIkAGC`7 zD_Y$HYd%&jG_^?Ow9x@37kMoBeF|JR9NEZP#UDnQwL)ih%Dku(L) zm{rltCK#<2TT(Q%(%C)%BRr(|;KR>s?~rd&EYt1O)3||Ce?_agWrbITNHKTp60?G)Q32t zUJ9KtN7-$Ef96VmMz&++>uF%;EYB zv#@j8lNVv(mh&IU3K~!q`#(okf2+OKtN;Iqtmyxjtp1m*{+F!&m#qH3AS+B1imU%Q zvSR&j+G&4BR{HDKb4x$!Y?Hb$v@R&kgBORFu+Y&8EK}3R*vqV4FVAFSrg*j&fxjcZ zWLV@b0gJL<6{GeU(9Mf%dU8k{@6T49vaU1sGrX=A*8N@(HW1Z-rtQUhp`YEQ#EiBv z^W{X@2pNmNEygRN^`f8>_$rh?%LxxIMhlr~UPU64UY9`56IL840ujNI!>3DZWDJF*_R{N?n zBrfzv2ccBp`8F((>Y5we;Pna76J@;@)`}ooM}ji~n2hin-=YGo)f?!kc$mj0_sbFN zeDv(?B9mQ4YPErF!Hu`?+(Ss0p}ZnX)87r3_PmKKSDv2{mqJ}*WkyId%*C9$M#!IMWD#YdoZ^`1MbN1v{=)bhr;itF|~ ztP^@Pjk)mpc{1WK$MCJS{^#GzlNCroVQl4JrkC&QX4b#mP5Rs{2{*&JlitHqH(YM< zXNpPG)`DtOR;@hA&VmZ1W$|Q35N?+eLD^8nL95 zit%0*xFUJP4)~*Csj9*(yv65dWn{^~A_-I1DeZyLCtgjlOZ`R9F+LJgPtXqSfdJov z8IKQax)alt!rEu)ZqynAv5#b*c=hKW*=k$s7z~;%T%)p{>b^;MUmioqYVt2+^-pht zL^pGApZHQ~LN%Q)srMI?gQ^+$4&U zE;%95pw;@{MS~8AXDn>mk5}ZbOIJT3xFz2xob!3Q>HzWSJvY2MHJPOmXx6p!^-m|)T@-%)`mc>$F~oS;XP*> z?g{b)b6LTPppg`Vvb998csE57Se#vyWwvW$I{;4x41h@&96>8 z4yhK3fc^$fcY0yyD(tMz_VALCf*td2S^D*Yp?>M25%l=O&NSvn00|^Bi z(f%3=PFOm)u3cSS|5qr;UIEtlT|hh_n));B~A4>F*42VpEu~siC4|<{o05|H5O! z1)b-PasK|=?}%Rt`YrNI+Y&JU@#Z8+t*7N2@zL&}bvAD}SPo-VlofUIT-emSFreQS z;Sol7AMfYk12B4=xti4xG+>tUsc(f+v3+(%e0N$NBCRsT|0bRvTK`Q4@K<4UdK-`^`&MKO_$PrxU|8JS1a1kT|di{@*VCbN+X6;CGev zKho+K1MLThw6dvIo%)f$AbmioQH)QG+h=aO! zW^djxhT^-m{Rquqx@Zes_8q$oS500J!E5%PP#11hLO1Z__p$I{=I1N@Wd{9ase(}m zIf8T-X=tR*Md%r{(-<&GZ@*B9@C9}gXZ`I!AwCA+gbut*?wObSEmzvMW$NM`*!nEg z8Az7y;G^ErufQBNnuOFtwqBehRk4!*r*3-*GqHwYK;<@5@pGm2mF!Dg?lCTFKE0%K zei0mz1PcHsSO=>i88>@4b5;`uL~7(WtwE?J65)L-Q;jT+wx6a?3`8%k+S}~cs zXXJ(y?Avs1Ab^;S*cLo1W8rBv&cf0z9uiY>OR{<;Go~G&0s;G|<^`K)z(SCdvOJ7pO7NKNsG>4ho4K^4DDw1&*r26ko`zI2{fsT|Hv~ekiE~7^~3xpnrEEE8j zJJ^b11#`<9ZNOn5l@TexoI-Mr?7B*&&W`y)7~g6zmH-P2%uLSE!?!l&fQ+;=CYJtw zS=TK8dcV{pJ=`o9U<2v{<2M9rx3cGjJ{R|C7gAA9lC`JbOh$NXcubVtVr;rq<~E1FsGV+))uKW4*dwCoJ)jWf*-LmU0xlmEtuyB+F>DU>_%a(WG$9?(I# z9PdyVs;@8n6DEQW;fRYNzpvFl|1%sX!}NC`Q-C>?e|rk>=b!N(qt3rg0cI(s--!0{ zuCePlhly4TwWA3mgFYh|c5AgrifL%LZl6(E+TE=!J?|?24kWQALH@Ms@$`qpmRZxs zf+@bvqW=7unR?HEp5e4Nx^wRL>jCpKqpv|z0vj%N#dU#Es@Ztfv7!=W2yhE%j87^# zZpg#el+7M9mrBxZFvTC1m6k1P#?C;v8;gLWWCE36TdJu#&!wn}VP5xA=KlJvAit+Q z5fC&6unr}?i^GrBMc*c!u~!*GYeNR`l3k`wX}D7nX8{JNdNxxY$ed;X+OTZGP7Exw za6DA67x|L?RBWu)tmT@Nv6>o_geH?V6xQG8CLF1MA0ATQeHO3ViElEp=(p}9DPLqT zB5;j~+&I)fsFMk$Ep|=#BkD-%egw!H^t)a-DIAtai5J(x_t$V z*`5?aTtOUhY~!l@{pY-WF5*f6IH;)_QG4)I2HbE;#itE5Kk*=gTBClkEx=~|o+$De z%{P(Mx>?>RL#J;q6$gAHE|e#7x28`H@5D{woFg`OkOC+&-v0*f_6VEy+c)jPQf!I8 zA!3osi_c9=(ySLTK|Y)s7IDGlZ`gdPnc*ubX{lkUUU5chN#9}06ED>7XY@qW67qaYZ7sWC-RgqOjRPnN?{If-US6 z;y*Y8HDII_>MW@-UE+SgM1Nh8*hcZlQaWPSxlw#zWWK2k!eO!)aULxwp|3q6Q@@ge znJ_PusS2V8_-px2BED4&pQ@z{k~kw2Hj`bL3y7I!0(m!uF92Jt=w-)Cq829;sJ~gv zypKW{HbqE?=Y_8Krus4E0tAP$te}YK>Bu-EzeUg0SE%<;3^t>sGD6D-6mejYA=ohu zey1QUQl-*SEx2OUt#yzY#2Y|%6bu8e5(LXweW9Nb(L=W8VjO;;lwr0;6uDw(qBO#{ zn=-9$b_z~P-W4s<+jX0bkQ*tLUMU{mRy9)m5XtH;zEe#HFX3#Ws+;&VN3!5uf-+3m zeElXOm!@ildi_U1C#28cWfk3)q2^X>xt8Wsei-`daAp>p^0wtPA~w6eTA@+jz;m+H z)my69YDHm^vd7|t4CwNV5wZGN;?gq-+aXgD?J7ytiK++)D<8t`rm#wB!*!L zEQ|0!+ZlYiri|&qVPQ8Y9AP8TT)+c*qMnbqPi^TT7TqeJAy`R1Uc@%_c0E%YJK6ZRLJN5&8S zS!85gwq#4vc35Pnbc21Nm9_LC+uY9l3_=Y}8E^+bWpS=%DwVb6Pb7TYm?{ zfl~)&pB&vqzaC$Z^QU+J68)A!A1m7wG{$hlb(@is`$qA0t4!S6J2+~;NrAQ<4jEoa zUI3PFkn#w97EX6iSx2vbWA@j&gAEX@T$IFA6I4w|B`xCdykCuqJ1Hju;JhpppBGj! zbQTY@_O$$sVOyy`C=Kj&4JzJ0&GSV6%ES2v>Qs>;Yg3NW8Bbf6WZZB(&{d(6X=NvG zF#4Jua-E`x6b$8z+7RLk)i0%dec2*9A&RGZvf8^Qdcv)dc!Ch>6x0pc?v7HT7sI~} zedhLlDCimI<0si7M`el_tH=aDwwddKSmr3zZY1w9K6%vu)>X`K+kGDkQb-eOY)lNQ zX+B!lDH4@o`eE~}31`K?VqDRLoYSfD6-;I8NFfL))8B=i3;bh9vY@3%ZUI5hY{-*g z|K_DkuKylAt&|BxvG~W{K!b(6DbmbC@viZb%Ek;sB{}fH_{PqG@F`dw#Rd(Upv=tv ztToxo>=(mChBOt;$CK!qw$)YyG)`FOABA2|B%I@4L|NXJ|5{qavS|!BjbWs zB-E@AP*=+J2uzAa3$)>g+wflfDTdKE})f|Rz}u#2jHr5G$# zN~mrVjCu0UXOIneCNolQRw#VWsUd0*$_lcGtTe|is-LDvG~D|dsJixQ>jKSZLYwYK zM2AZZwwFB!W-6x&Fc{7*BOL#P8GdsmQYX@Lt_7-yAaumLWeQ*hjfgutULp#w??nS@ ze}~SNIH~VM1-r4LWX)$8c7PW6+rr}kMPF3jrKx_LRIt-x{g?u!3^pz zFfj?Mevyhg-%WlO6OK(-amR-qbNUq~qOp!4e5u)yh98GUch2t z?`_l?94$lV3{w>X=*!13l9X(bs^ccj-iR71hw#7PKJw@KG zNAQ#T0BKAtWeLgRu}O}9v)k7UZ&A z)1tLCeErfPyqzeAD#e~rEuI!us$NWL|GVZ}Fdy`LUXX=O;T(q#@OkXzX9+c)#Wv&x zz(U`FWq>@>T7R!m#uq0-GgMqZ@GSA#1aGGy)o(oY1w^lhs>tbyov|vRZSE$v3Inut zL#*{pGN0!MVwjh&1RB&>Hh!G8{x?i%;@4hHnZIF7iXwx3elT;fiBXhGsYkrG`a02C zVRmrJI_UF|v%5sr6wQdzm7!O~6N$ZY+|zHtG5;3ha!!6iP#Lk#5{$fI#oOxlPh(L8 zfkf3!h^4Cl{M&^1=hOZhA*xxaFN>kIkC6BD1bzNWg&dO*q~E2&clF^N6bcp`9E}3a zciP0$K6vAyL}Scf$^v!#<%bYWxARBSnVCHLTkD;|oolr^ov&o35*7|9TGb)i;!C}z^j}*nD+ebq&FJgLvC0p@taooYfo)4zGNmVAf&=6 z%l2llDMSzCx>bx==_t5p`0L3L+Be>ujwav$vj}N}6uj!AZa6!L9&14)?mV-1y|o#~ zXUoFZ$;J{;goc;wC@2e0f&oiYM*2l0N=8~9r`=eGQBNPI)?zLNt8XD%5J(D8yt1~X zP@n51byH(mS)7h2@aSdo^xrWTwVx-FW>q?))yNRi;^nQA_<_O?75@H|*V&3jJX;t+ z!6$-6CMy_+83^IEpb~mo_OZkOrS96ea=26uuS8Q^Kg2D2*^G@zz4})NwIeKYv!C zNl~`ZQ`+=Mi)Hp-0FW<8#RhSQCrVXcEoiC+AT~R+w|BQiz!%56CLNEoWAs5Cw-xJ(UbQGDowB);7jvZa`bHaSbsSU3b zd8|MP_z3Ig#*_Rthu}f$^N@{KeT9`<(b$hdp9(vK^_Kg!bLE@f3I+DY^e8>{)SI7` z$Ay?P)*-mJYNU*|UYkpqrD}DE?6-mL@d~sJ+ zu;AlTRmtjIl>wU2baQOGSCT$iAi5oEdWBrC%=B>syI4h1?IBkq$83}P+mU0_d8rE+ zSw1#Kr)7_UzJ^HVE%P@%dy{T@NMkoX!bVA1p5~@(oG6(&ySPu_nEXpn>!K)=(nhMU zn7j)c90+vXlsT}P?Y-w_p8iRh?;4H`>AG+;x+!_!HQQ(2hwan*=^B^zDilAF_tE0M zXK(SJFBfai1ju($0m+FYXJUA=c)5vHyoiH;qYXe2!Rm)#}l=_zj^;*Qsb zVJTnZH<7mQ0X^bATe)|j4JwbsagW2dZ+RN0vtP$ZC0;W(dE4&w-43bsN3%C!@H+== zvAt3jY3+DDdS6fM!hFKiFeE&yLi7sLkwRbj0Dd*<=4`Q-Z=lVcU+c2Ss`x)d83xO5 zJO~o>dWtv^QF!NQs@QLQ`A0mO+E7(C{~yDne_bd0^Fkw`D4H(_1}7;{M3!v|jd{<6 z+Yu#$l&aYrU{6^KLrbAt>$R;`B1%O_1;^qaV(vTQ!1_ zxEEVm?C}=)z?r{Gdi&mkH%_@CZ>a$@zM?hCGG_L!@9Z{hKrf|qX40mwIvM0Io$5nS>ScX6hixGaZ6iz$ zi_~#a7tJDKx0I=UX*@lBUb9fXXz$`to$z{|i5WJ?gg1+kfudw1Iy!|>0ASsEZ4aDC z5?y7S%2B&CBeERAa|&TM_70Habwl9fk{Q}n%4JSi01;p`l@xk3LKe}`gFG{Jl1|7~ z7%~+C1dW6=TZrDc$mx%bu<^a`ucFfN9<*ONcNv$d(u`)vKAts7oh+^9FbHRye?oK7 zZ`$vXNiZ=IFH8tCY4J}dK5P-C!-O5+Y*>C+ED%zE<2B0UG;8>QR#H8TgL+4Kh5hF^ z^Mw&l>qA8DdgJQ8(HfY4g_?+ffBLvSHZ!ne(mJT5JtisocoviR#0^ABl<)Ma3yMv3`(3X=c=gnz6E|;U|u7xuUU$FsT+YG1>y7 ztZ!;n#W_J`^+r<;Lo*GS=y z)J+X)i(qk^1Ya^c5H|h<_`3sg-~&$n14{D2^1waK2HZ+7PpL&$P{1NIym!X zf`-@T!-;ZacP=xTg=&l8J56ZsH5YzqyKKQTFxd-fz>Ca#RUy-+WpA`AYDQUv@|xSIPGO7#;n`QMA9Oqo6m)$fUFg-Cs-GDM?*0 zpitQ4M)N=CBowMN;F8Bss>jJb$iGhBu@x3&%*0Q;D)MJ0nrsjDe}3+6oj+UQbu%*+ ze|pXq_zlGrOb$vdNL6eakB>5uB$FL#Y*JPQs1V$S>x4*e|K1nBSb3pb0? zNZqyZD){CAV;+)}@8PoJno}@^BvjSY-o@~LoB>)tuKDsO@THD4Ni|%_=&|g=Sm;rm)b&{vP zqJX*~9?O}<89%Wn-*g<}v4~l5L^waiPX(s~FtPD!qFj>1V6#?`hV=2!s zwq5epGo#=Ap|&t1b26b;2PZBH5}aC zT@UUS+}(qFg1bxb;O_2ycvDsP)_bdZ)E&2a{JVSH5Ad16+I!D6=X%!s(XPXz?ncZn zCgmw?y09nE-Iga$?nbZ>FDoY=CsB~se?@U88gNqj5M>PB9>scR?mJUu`tKFaIP4zP z$W7ObUeK&^UN7H!grxt(Dc*9N952Y1=ecKIvR_L+Jc(JR)PJl)D_?3f6)I6x;0YY)bahtP{S%QlunouCxVhjZ<=Xd=$$jvNR@nU3{81QA*o5RD z0)kP1N{VU4hM-;V;Z3YYYx`^97G!rNz>jQMuAnF*v0rPLnl=i*Ea_6 z_9(4D->S|vvg!gxKyHA^@)t}Qb>eg+qVj>J(>QMTJhb^;)+l-EsL7^=xyu-VI$Fen zCUUoF^IP-xe0#@$mGv~}{j3ac1}_kr@xyCQr30XDL5l@i)Jb(a=@n~DjNpLZ(mCUj zy5Iqd&P{BqWKM7?G`e8*e!=4#pR$iO$W-otUIcZr8G&Rh{8jGZY+uMfUu2vaZobz7 z?~lg*=kXEyKaY=2?Z92tPj$0b^AhTxKl4#2ziG_(wtmCcQw=u|b+(D{Y7H*+@sWmM!zpi41G)ivD=s)bN(PQph%0St910W7q%!up23C-) zM#yev-Vq=8bHPkke5e9W@E>Kx$t3 zcczy~vR#Q}*+ht*gTgzLeMFHk2<(!KnmoC&q3ZyF1qlG0mHyuDBH7%x6VL7+aEBDz zoNT+RZ=@MgT~a>NE*qfd6d$Do0Sd^cc}i>Lb3d`Abh?2jMy#W3l^OT9*RWTgwMgI= zD^aN?To4p)qJVcw8KRUw5)m<&XY^6c_THi4$-&+5eHdCC)C1tTHF$>tx;t6)k%Ou2 zPGKDM`!XMWV2_E^%5epYxNhQ>lT%@@7-BmOkkRYF+oROek4RU!O>01OAyEHLRS@Pr zVHj?pMt;s*Cl}jR4}<>%miRNoGXiA|Z9r0CpP@v4+z2@9LGk*2tjsNsO*OzD zK{`XoY)E=xSPEZ%JBzTB$(BDuXFZ&w+m<_l__8(HzZsa4vGe0LAjeVu!AxTEWPGmS z;g=#zc*k)2F-^;Tkp5sH;j-#r71a_cvh4LHJjdg*y{ZXo=ah8SWex|tKeXGSzOVEI zy|Wlsq1qe-;jRA>ex%ZaPRN%W4$5!B&~~Nb-=`hzBnpSMv0Sjel@V59rwsor?z;#X zV#@EgK;OcAJ;xxu&v96{JNy$3Zrd<}XtukGR(kXagn*u|R~)*_YrX2T&eq&;0jO?n zP`CE%cF(#2WH`w^C2?V}7;_^e&V0xTqW8h19dAbPAv93^oF;33@t7*K*0IAb_p>{^ zgRYQGtMnis* z2S2gr!!EONAfm;*JyC;M#?hbB;Ksx$|4ZWA!h3z4J4H&<#TQSa?o98b%k)z4ShKAtJuupb`ejh zc~l>fACYgqG@iY_wf!-z^?Gjm;c+y>>ouFt7mShaVWEJ1E=vu=m41a*abKgjhV-qX z27^6Uak=oY{LwsSlFqU?@TsDbgcNDxX2vDLkoKvB4q(Bu2m`z~(vwaKS|X%|nSO?H z<(O?kB{m1@lFY^sSDTC1yhb2zPmxYU%yt$i+zZ zNXVL>53H(EJ4i>vJ*HdV)@XE_#1 zdJt8G!`TmN@8um^uMj%N+}Yj(^VyxR$KQe65; z&@c@d8vz~17)1__7QjOY8%MQ1!^RF@h6{h@?1USDYdiYYl-Pb`YE`H zn0{cfq;$Q`de_AUo2PM?NruElQ^CQ&!jqHpiqq&ESpYJcig2sjWVPB8(08Kaon^2* z=h4^qL1a?>{f=P>bhnNla+2ZB%GfRh*|r_DtztMF7UGWc<*j&XtG>D`p%uxls*MLv zg0^BfpABZ9wRR6XKtVgVA^NN0I^1HqsO@^OA@L9iXxV{0Z~!6iCse3h4JC9sY4Ef! z+-J*NhZHWERRrBBfg-19pmmw4HknCM9R{m|fwb0!ctp|nBvG`F37>tgmCpPU5Q5}` zw8c9NvN=b`8&LaL^K0nr8< z0cQmpYAnd06iDS9x(cGZ*n40#AyfHQG0@;&KgiO;r${1udrU4h_|18Xe^)hRttrgO z+7CEu%E3WKo)LLJGbj@6A&@eXPUsGUoR0a0yneqS488E8CbxlGl5^n17mw~F#Ziwp`i_lJ`~m%Lf9=W9n_$Yl)9)I4^!x4lj0Q%cf#T zE7GrMn|q`4v*m5dtq6@I@SryJ$eIJmX9EmW4@o9#Uz41F!1 z?+T>~%r&K?ix^HZjRCQbqOJ@*G)}@1$tF}IBFqehp(kcXqydGtm zZU>ScM(OokyZ4QfT;wY5WpW>NWlEb{Wq7(zU||`ehQly!rW{Ibdc+8Igu(dZXK(gm z4ICbe8cDiOD(<|s{4I6o>~Yi)ywuS=;wwmOyW(H+>(svprdzT1}K?oF1=#L)fCcO}jZQUNXL^;qgQ%HcqYJI`<{ z&471xY>p8dXIZ-#cSaoD5pDbGKduonepGWjtO5x*cXXK0^11+dUCd1D95l}d@5F8b z)NR_h{=7}p@b+vgdHw3u5_qeq@aokoJu3@319JmCds{0;W)^xneFH~2J#z;GJ3U(~ zb8|a7prxaUxsihjouh%Ror#qt-QNJ{;8_)>e|U8uFIGkdi4VOeqke(6PPq`4{K-!M z0cGqpq;nwyCSjEr^IHJ1cAq2XFXHM?`i_oL2%0xsnxagkxDT&9$=91v#4cn_*beST zTFxKRU5t#bdq=HSUYF{opOAj5ic%L|o+C5cAIzrv^l?{_G`d=yjF2g50=ft>$v*g- z@IlkbiELARYk`|a$#CNB8HkxjQMd^>oD)^v)T2Rr`7rcyPh4K;0yiickgn1T6^)&&ualuF35L^ZSnO0r~% z@%_1{Gw|;sMXYY610i=gyeS_$n?NT>V>wP}dv4*CprFs{({~!s?S9yU_FJp&@z$Oh) zf)PLYDm*Z)w#a_oXXj&=z6kaXqRC_}TdDmcvW|JWI|!Nc0T_5){K46-V5~BKsO@qKg8C;X`V&h&R6&p5V_= za10YkopP3Omz8E>ou9P(GQw2gr1FgcR0TXUP?>MtUbH*bTAFTue!OJ$7TZ8K{9Q}m z9Kp@N%$;xGEGCg6q%8`rrAK92qQo$)Gd3nULIx~M)M0aHY>(WGtf$qAL4^^tR3zFD zPnDI{eQTCcgjL5#|B~!7nc@NcQcDlf8E*tKX!ryui{3GO_xg=e4WGeIzpYc!^B$@B z;52+FXFtFXRcuaLd4UEO8@;@TX4X2gMoEEpO`1!IjtN(av8MUzY=Wb+0% z5t)g|id`*)lW48-*|*GL3B45-e`)(7W4^BhuE4LpxFGEkmQ;VO8qK4=@390IYsn#W z)@iFVZc77SxlH0k^A52|B%8l)cf$#I<&d*rkv}(P2g6~~5I7RA`=+DPA}<=E zhiekUhjyo0b_*3*X3KQrTqx*3SsEx`Uc7cvIa{1(EDiJMqo4U~{w-g9zg$mMt%x@@ zaUqTvadpBbD~46MRvkJM!Xx+xe%={Rr@mgHBK14v5atdPF8z1qr!AK6v|e*%Ckw`G zlS{WblZaJP3YmfQUmhR=YM>g44HaOi{88SusE+8a zcw`)@3`Jkjyq1!3WQ-4fX?>oF4QDJ%^ix!24pC(UeAbp5=*QL-Y#q6$Z7p!yj0u4S z+*D9^s@#om6WvjF1FXfYUZd1=wy9cI+%+_^>#DqPxjuy~%H2XgqTe7a8YK$hb11v)i!3Qr_(sidsO$9ck^Hk4boUK?m!oNf**g^jqL@x+7qnCS($!-E}{;Uj3-mt%mJScErG|n-S1!1?PbZ33&@xlpwf95PmRh23Wh)UHQMP^$0@%4+RHCh zu5TI$Gk*QLVxK^4V8~4U_I*)tf$}Jr1S703q`VKb>YKzYYl+VBUMf&K&_t=!Gsj&o zSlJ(FkXj$T>4pYmY2EEU_&Y5yU?^kkx`s*TUU2T{9oDNTU2P8R%4`=4puKOd-x)%uN|?I)ch{SB2j3;!^=SCdUBC`5w_DHjrt+6o5A z75Y_wGH`Sx(Y^`1$j?MZ{tcBc4REbu`2#Ay&HVzE&uDLZd}WQKf}Rc{UE>cPQ?py2 zIV4#|qd-t1TpPp8)5YCUEM^VfD5|VtO17HUanty@S;na`nuvu^eTb5yg|2Z*ekzs;U}ub`j})TrH4?5b0CK7`LQ>!J2@MYah8)C{=qTPP60zDs-9BU0ST> znvrh$5lakff4?UTZ*YTxENftltGu1lBOA?uGLONj(~$l=z>EU$KYZ9+U=GvE_FXUh z6t|33at8>1Kup09>WF2FhmrYIV|yzy;D0O49h+Gu`bko`!l+e{2qA1pnS5mO05_a_pt>xef^A0q!58Tw^){kEF@5_` z*VP8Cnp5qyWIH569 z3o4ZIQ-Y_lo9TptwA)=HZ*hSWsYDPQna_G+(o)hr*R{b> z>!;_Z>+O|Sv}ztUu(?riQD!w#ke zedJFGK`jJmsT~cU6liz+(Y+WuL~k4&cc@>x*)b6EM=Ow(RLZAI=A*CzUr>ZdETAl% z`%~+8>zwzD$=@0ZHbuqe$MIZ}2XVfsQz>U>N{Iu%u!MPlIWYdxpq&UHY!mu>)!HNC z1K*bd#&p2?tM|pukb&qCJTqls{jti?r8Q}_{wB@t@(=VEQ|G$Tz+6KW%uO$=pIuPl z2_HCPY{X^YP+*t!*|6rs&@EAIo0w@M_TDuqwFKbW4s9wMO3PGOTd|CmHG|@-R50G| zqoqLI!FX85Qc% zmlH@7t!Zcv~A4u>x<7@Fq2=iD;aSbVXMz3Y2>KVQcGHKGa8H z0-O>R(QI>&Q~EY@7wod0aWFAeI$hz{UK~?i+H2Cj62gkQ;{AtcFyO6ZHTklP$d^6Fv@#bAAa(ywdXi}*et<*gy&C>J8Yeh>vZ--i4XgP zapmTyXQJ%wLiMW${RRYd@%v#bLS?O9bCP{HTE^Jpe-zy*#FF7AY-1az)ggH)Mu z`}j&uFMzjJ`1t5-LS-f7lQ?jLqv<6RsUV$ELtVA~e17d8e7DwuX$liDDNN6isL)*P z$Qr&$IPG5q?pbRX>7M}1t}Kd##7JiAeCH?vSZTvjx2hkVI*zpEzN}-R(4EjYWcsHO z_(mWT7{N9g94(F~Lgh8-prtZpFiJ{0xgv4H?Y_@0umJPj$R=*MA1`0vEaCB0*D7E* zi%$=?ZB=X!{rn4@Mbb_K3}<;H!G2=L_x##J#I_fjL3)~Be1peU2hIk<*Y$f~)Y)l8 zUb}oYtQ~U~a=4&za1xsNsG;8bT^_^8t1|$RHvYtNRtZx)i1j%jfsu5uL9Jsb zKZI7y9t6g_U1D{UgV`*oc5TIc^JpX3KNXqVr?h(RD=HNM+%jdC5M|WL9-I=YRn=dz zJwn{*PVwL281Yjkicq zMFJI>nN^rzM9n{^tI@toA(Miu(fx4+Bm?u^+@MO|)cefEKrmC%Q zMiX>j;4A>$gy6X4C!Q_;3SvQV?YI*y+4r%jhd?x;H1kV(Is$veaT2!{u9EAU`tp7hL>kkYwNdGoj z|J%gDKa%yJf48jTf(zEC7+VVSh9R6Lf7ks9bq=8d%KBdF{tnA?w$B&YpZ{0Z|If+# zEsHU_?*GdA|Epy^)#}2$P*9X~rpIrK3dF*Zl5$o>c;vs-n%&mJN~!%zWA8Hzr@$K= z3jPsf+rh=g96C60haa?TCOigTQ;#l4T~Q}+1Z5WJhcBdaPu=eJvy#RaF{$*9D=xtG zrr~ZIx@6(Nn)__u+@!5KRWFQt3AP8j&kywBBGyxAyi#jYN~gTm=CNAwSiSvWNHw;a z5fEqIL#A1`G5!w`3i~@6dq*LqM0GV0K6#8IParrFP0H1!@)D-F4^~fbGzO9hHr4(~ zQYcz6JAhlXoPE|1xr@mW>aE|%HTKFPQ_rI2uno<6l_Vt&1;SG?C3}P>tbW?U+9_Qr-pk9JP!b=lch=H9fpmmTm^5eZYQx z+G4bl#C?B0TaWk3at*O_a!gQ@{3C~1`ASo3*FE88mbf6cdXC}{WSaOTX*3rKOM>t@ zJeIiyjGM*z$ku##Pp7%toH3dxO({Z8WoE$oM=1fU7Dq1Bgj}viqF-(Yvb&vnI#d9I zYvrDB5)kxKC%dUaS+l%^QwD)4Zk;OYHFacdtmO%DFvL<@i?#rUClr(-Ml$sDZ6SkV zu5^H7&wKRpt#(@_C`cfu*R3P``q#o=a;>z-mB0iyOUVd3Z#HfjA1ur1Skie_AG zRzi{xxn5sq(z33e+|`H1?MwnUly;vKu_qt|LjhEt);%)r zP)uBjOlL4c2J`9ANt=kB69l<|U*2#FQB7}?R%NM1i|a%r7l?Y1mdsXbXGcQ|8&V#U)dsqH0qoXu|HG}1u0B}))va^?#jT$=FDmxvcU|%R6bT7m zHQfDE&m7gxE7^aWtpBA9B~N+(Z**0;sI`)A5jh5!owFKB{`bcOv0R$ z6D!}my2ky{$^?RijFjtui45;`1OA{0AF?qxEW{Tg1D84|$Gm!GHmqL`Q@8fptM;E+}5Bi3(ul0;I z#^GM*?7}JPngoub3^9(Te#2G41Fh_LF~8xe{Oqvv78D1A zAe#?ml$c{ZAC{OU`8Wjvl@6sBM>N22)%9R{4r|qJEVd47ceVNnQW;gKZxfC)>)?jo zg|)y{K^nR(3|gf_@tlWasg#Dy@iko$#)y52EdC!U05rm8n9e14<$B>RIUIq4ka}xh zY%rfaeMX^VF=fDJ-v^j?$P8-3>>(|dHk$$S*@+IV2Ky~w5iwTfn#Q7M>r6RbWkR}Q zY0)4Ld;$p6W=-hw(Z^RXYW`pc&A{#z4{dxH`6OsM(VggEX7UJzIQg7nx}M zFc7haq$NieJ+VkpXj9!JiDE&kT33mcbLt1SzH>lmze1B%NFqN4CsEZ6~*}RmQ_PH`dZ)q+dH=I+VcX5;ga6 z7hUkgP#yt&S5Hv=sPBgPIn+v`1m^(9JfkH+C@sBX$YOEa8A=R(>@RLzxW5FVqNZ5G zU2HUELrJ6QL1~Ua#Oz)zI+w}W8$nQuR@Py+g7V$EoXfGm-u=SmapSRqXp@ssf^Ve3 zGi)((;&61IqbbJGSaMiK6mY6(@)ylml^}Fp!Q|^~waKd((Eh0uZ=KxC(N3A*Ev1M| zS`?YS8=cb>y>ex7j18E3Gm0ClPRR<#+=p`ztc;~^K46M6^{(@EQ_7&``P+gwhY?dR zEL9(Do{uw8Xj+$>D6#{!F~#hhDh75cS_Zj=-pnX2TqN~0ZOZK7a^r&-mPfQ{j>ZhkfRj8FPR{j)@6lNY{d&hNeyI)k{NJ^ zyV5G_RiVk823dVJ&vecLyZ!Xg6WaBnMg7)=;w@qjq3q)8!H0GI3S^jYCz{(&h zp_Tpg#kJV3;FlZfjyOO7`Lnz;BHZ-?`H5BY(Ox|(1y zWisOpxBrfNu;d5Z$ZVGk0jJB)_-y9vrar7{~lTY=VOWgRo4F! z!u$Sj2><_s5WdTXK;HKa?mx@=ojD&3Iq7T76D6pUn0H`V_YKGf(~o)@zWw)Q z9qk0Ev$iH}vY~%6T`!~A*+lf8Gt;5|2;p1o4NM$WU+e8SXQMg198R;eKKK_RGkwxr zVLo>^xI!T}Hqb!=xRqljtZL~QM+k0rzRQvlf>Q*IP+#ZYD9{1_XgL!zM6VGYaQrrJ z3pD6HVX|}q)#nd?y~A`8R^Ss5$q@r7kESQ5>NO}qV@RnjPTJ-YAMQkQ4R5|zD?_1@ z8|%rdq{Hg*tSK?(hZeB!rH4ubK$odIAv*p5SDoR=fE7PbvBjqo(2KjT+_NE8BC0$; zqTW%X#Y5|f-n8$+Flt@&rx2e1cLS>Jq-bzj^bI8f5Z#<2?B&$-T4T5Sy`1uB|QMijxl`CG9R zAcadkf{)1F1b}+JPIgdw&lxPOEXkgINc7H(0L(h#vYgGw-?EO8M4=&LHTWXyVLFP= z?(x{{X1naT5Yujb)l{~?MQ}wJz3|6n(CrVF02)pqejxy>kcm~S)Fn|oav;jAR(hqK z%R=_z1v{DI#VTrJLrJc}=QQD^Qi$EY_qH;Wt)X!toL}|XYWnd*J#(TWW~qqfSkV41 zgdZ&e7t_7Sde83=KIL}^AMu|<_{u+IUH-SMgAwUqS=YEcZUW2toulD@$oguZ@{6n& z|CaSnzbwX1gWije>O%zq637f(Y3~*SxqgI*_ap(J zxj1QS6|GQBqZWQ5l`;4IjG8VklmmIZgz#JcHH61}3E|Dil&)VwcQw~7J9_QfQahZi>%)h|HBZz^(BNC7;lmIFCjdMl-(b)-pUl3)(WBQtI;t} z=i}EVvtDpB1bvaEv^UotVHC=+tBmpGEyJLoxRn>4-GmAvAqk!M4K2CF#0}A~l(bI26Elm$W-keFF#_{0MFIEY;-Amf@0WTP-He_dk6$3C-(>)^zh};Gqk1uAO{Ue~!leml zLzV;_Bcxd9HysR!NBLb|n#`iNXSRqT1qF{24!+yY?Ph8lv8s%t&tzaDGZ-CHlf!!k z#_s6XDwhiKVI_97nWNv2muJJ^0LF1Tk?k3=YfP8KenJhdN7!>=^zdQJpT2$n3EQny z10$BKj8M&l(+pFLM-LD^Rz>vDKZ5QZyQf*L-@7fwXZE$<7k5hz6ejxOYkwFSL^ zTz7yBgTkza!$>9ZfGoFlVHVoIHP3UpDg3m+R6v4|={TIP!_&qQ2*5La+8 z##ijUXIEbtEhf(U0m1XZp;2@?ue}&Q3_89Qq3GCZx_!S*hG2IgNez}K;gPCPUXvw| zNTbN0L7;@ulOH6vKjK}_*Gt4f&K(Q$VK}KSkx?_!>ZU5-o2Q5s*??uoZjbk073CjlWw&Lh_492gS{aQNRHPb zcbF@M&OTN}kR%6yk2MAF*p>Qd?6%X}sL7|dxN6fd&z8K&mOBiSNH1?nrq4XY4uIHjtKtmvv3>El#&I{>H?7;dme60P(6oj5Bd zW%*lf>1yH_w0jN_^I4Sa#j`(cd?{6wnx7G^KS zWN>m1Gk%FUhrjV&=$y8U3nVu^r8!QFU-jC>uo2%pfoXU=1ZpAha)R3N3RV@y62`3k z&HFo^@Qk%jcw>ReMMlpY=`=mL8V2qdUDQYvt&bij(ATy1@BXeINUcF-bS4M!XP3Q!`Jr`~JoUM`Zr5$mZZiB_TbBXeSM3P(fs1h0`yB^CB( z5#@ab|B~8t0L(Z4ck(Mes7O%q;u{r9UE4wJCK2wuHK@DIy> zWA$FvO%`Zk`+MyyWCx`o_&$^ZU7DMyvu#g*iG7YGX`{uYL#IV#{$ZV=MXKLdX_gSs z)jlDpyY>U&Bqt0CQ_8$aVo^#L=lzJ<3Dh?< zj;a2<@17Qo25LpuZ%Wl$jdHwe2`4Ho^rNZp1K_c4j|&ACe8kJAb3W%Of2rjdb(J}T zhuBq~B4fwFDNgC{Q#ozDfREQ@P44nBHOr|KSzzKNhjMC*jOJVsuETduB(w>gW8kFz zHtF2$x~|jn%lhqgQp|Ujs+P(*d=6|X23s`>z4Kn3MC!hz9=Ozkm8t{&(hpQr8cWY* zN^2u)9Z7V5zY5Iz0%Eq{_h$e+cKY9WhZKHax!{u(`@Ifn3WL(fe8}A0OhbM?KGA_U zZxmBs*TVCKVem8M5EBz221XT@B&TQ7?35?lt01Y}!5=-mW~Ftfrcd4(3BWUGSpv}8 zXyS*KYk)z?+o`S>6)RubAyvP_gIq-J>rVXjAh^AZPV1JCo9otv2<;&bU9yHXCd%ct zdHe86EAoG!gX(T+>$%G{@5S^|<|joNDpBs(I{6QuO{f4K67pizLs69Rtml3BDSiRi zohK5w<##kPH~biuxymqGF1Q||M&o{b9sV5;;f)#`;n8QL0__s!luVlJzQ8I)9(Xxi z7r;i}^1IHwbx4g+E-Nhw1HqG>cp~+xS*9NJ9*#S*c$QcV4*6jXj8p@3n&}9PJ<_Ow zGHmRLM(iG&bOMnCR-q51oOlGiTFxKJtQ!wPjs}re{AedQpKhy#W2YmiqLgH zN#P#a6JzEHSeXwwYbnUdIq}n;YT{sSA9n?bFK@Atc$&6sPiIt{xuXK>=pafkf%tuY zsE>7j*t=pm;~KmSq|Ow1GO@u>&7vUQ_wkl=bkgpAN@aDq_fc5d?5nkSNrVf^O04~w zS)}o*p%0*UEY6)d1E0TD^|nlpR6FcaT4sB1H8w>+h?I)Ts486OP~Y(uVe(of8p@VD|4c-Nh7pb|hp&yrjC%I(kjX7`s+2#bIOby~{Va3jX>|UE z83EX^#O08CjuD`jcEb%hsecOlBb03tv?4$`j(O`lUXOkQIyLGJ_RzfHo5zb_QqU2= zVY170n$)c+ka*QS_?5V6?hTL7EmLsUWr>)3xY<{+upyG5LW|`=yAika^YrbSc2BQ+82k|8PW3#S zV%0h&6IQJK6pfOBDu;Z7uP40g;Eui zvSs%0g69;nIvhRpCNZM#o!_teBthYB))NDaY5@mdhY0UKD;os0wWFmS57|?q*YU63 z)AylwRN~Qw!J-?UC?5!?HNk>9W|4>DWrAW&dl?7_f}jBqE36{J$ARTadxwT)+Tj7m z4hh5ilEoHv>Q&8!R@yKQ%)ugvEni8Q*J@qMT8HUvL==jwqkrJQq8SY7%&49Ek5r8z z$%0;uj|2HY?TOs61ef2U!hEz*qY19RZ8N@gn}d7Hpj~y(*^G1>7sMP)8pKnGw{e#D zAjiiox}#2!g`01y_EGMw3$N0YxG8O{n0_o_T@Z2X0u2dVV@r?Q2Bg}dVy(e)@-_I z?udyZ@j|#jt?J6OuC`iuzvW^UhBGz-`IV3tulOU|SfnlQdkzNrO?Up935mG^&S5sB z*_VWB!+y;Y*@)%+ZYfw(6sR8^d_V550(tPEP{!C`fGmH=o{tSC$HyTFJ$|z2O1I83 zw8A8E3}`0Q65B|KU45IvaUqR{krKg@{}XkTqd8g|vAlD~q*7_`OQ@t-Tc57^jXL6! zWIk-gV@zy+o^9*l913&v{AIRCM<-t2f*0e0fJT$zWv!ePKfHrth1_-q{wr0dadk5+ zoEem>%Jy+cN5uDyGh;Vz{$4DFziy6|f&Z}`;1A8e`pum2vcA`GeCh#E6 z_@NJ4L>^ixi4>>rx|A0X@Hy9t;$YhI+Y`F80_#;d%%~Bcq5Q9JFXhaG{=WZaGcw9~?lPC@&d*e6 z3#js@X$G^cqKLyrGcxkh@o%Oa>BEOE{2N_|V*JJ2h*#J7==Opt3*6ty9KMHphA*G2 zFbS+mr>TJtnOwQH z1DQqLW}EFHBiao_E)tUSFrA@`iHu9Ve^KpX7tP%m@Uf7n^Q(X}DcCa*wK zfvYubY{9%%7C)u18n&w_NA4VBlFX-d#nNs$P)b-cH1N1pHRYrrjgT=8T1p`ZbA_LW zfv2`;Fq~nO$9!)=R{|paw61PRd%R^R2|rABSgNtD$sRkvU)}`nKa%>o~4X!tpQ}lULK!2(yR@SUd?ESY-+^1 zKOI$hU{4=AjMLZrwyaH$YNGIyA&b;)hue18RH@itlRlRAp(-pl z!gs4#^jBl4aGR!iqOsVxbGF2X0ikgbx+3(L-SqFr?7O3`px}d7X`)Be%M{g;(dUj} zd+JHW5?ZIHgP{#tufrz&afP`;-eTutVpA!JoAol=bL;TT^reEDR-@S?h@6RUdf1l2 zCQJ=IT(VS=w6=FV&5b79dOMtb%fA*7Fj?71{!)D-vqnKFRa>W8>}(L7;F{i}9|vpx z#*Laagb09&>=8mlxC9eXXivtToa+YilSo`MPN41l7;6d25o)3iq)i(fr^G3%k!q^O zKAcEf--$X>i;&(;U*Cy2a*43qMP+l%)!5jHKY|d+u&1-WkZ_9D``pCX`+k=an=?;i z6+N!JhkB z0&oLb0Um%y03?7D-H{Q%2w*r^6V?3^bzug7*}q4Kvzwq;Bp-)$(`fnsIR zD%>`wb`G03u^h4&$}xSZp5>gaNGly7m;TnZ@ml8UEf#c?aGY>F3&o~*N_tobm4|apaq*WLxPP9&5e{tB-0Q_<^i~ z9b?~m$>H5wt^L9QJoD$Sa(=|P7?&QO=Co(!Ay&_^$QoZjZ}uB|Jk9#GHvTCHPY~x3 za&;UMvflb)_TIO>z$nO5f|(1{JB@ENjSuaCjn^97m6g$rZDJjC<$4FOc%1rD(M)^x zfX^8C^rI4+g!HXd-nA;4@OX}hUv;+dt;WV{%RLV-{J4^HAV;Gs-lTN-kHF0hOpjDs zTOghZS+Dy~Y3YLeN#d!kDA{#tv94YWL`q;pZhtQq%(YE=?wm+E@m431$*COvxCgSB zx(9W1%`JI!&3%L5#HwCW?xaY3?qIJ$j3r(PF*nbQn5&1T%@G}EXJ~dr4a;G&W=h=y zXIbf?=@LzgJ!HOj;y#oloO2$ZH_5MQ;nxLkT2N7=P)|6Jbl{u?5^V~gT8johi;QVB zg{7ig)Zu=#7arquRg{$;Yjy?WYWa?LHOIie_lJGYe0j$7e7X2$FHrfQ`0)0cGP5%y zFO>Nte0gRT-o(H0$gYQLBHmGG6^JSwHF7P;EY2Lgq~%M5^2l<6Q%AYTTP8Br8dGtt z$fT-29fm)6ezu0TB62lF>#~RvX?f2JYw_pX73M~4fN0uRuP}XIy<+$mzQ`|ooWD5N z-yh^W4`_Gf>8s~P1I82s7y=2ncW?6iqad=b5kHF~(ocB@r7Vek>o;UIf89~d<2Gvq z<p5zE9|65G_M>VA(RJL-*46XmGzYnnZNd1%+Y0Ne_K$-IGVCe zkWa~pGKK!r)J@eR8}h31E_do@X95z7F@lEh&kug9BHefATx_U${uQ;6U-)huu1-_l z;nhWck-Q1=cyR9!Ib7fV$YY4t?tQW^$2r&K1_(|X*7*t_!6@*4i@vXp>bs|h$4`s4 zwuFfK$P9@acz`P=EH&yS^qC>3Grfu5i#WvP6Nf~GZizK!l8NGj@LLmMhSxHcgjQlq z2MEft!Z=SBx?^QZI1)_74txu76lD!?5b2`&1LH=}D0DOBv%I4t;*_k)6Yk)@e+2C( zhGExQ>Hg%mF&=HY_OS@bcrcPXTB~L`LATk0;WYGM{c?u7uG)8~4HKJ;Uetp5F#ln* zMk0A)KHqe|R!320#@ATN zW*ZAVAUdZXm~8zph%<~k%f)?au9*-`XhkUMHQ~_Te>DuPiZ~!?{({Ot8u)DI1J^_f z3C+}bB(0neYb+v^KmOH}Op)(9)Fkk2RVLrAW!2ERAf=5-k?g*|gMTul9Sn-i4@y-j z#jrcyncmJM8IhbJ#3V#7~hdvDr3~{ zmnK;%{5C`1w~A7w25-LSk=)U*iVE_ywjBZ1RdA#~^f9v}FfJ@B2#vZQUdPFx&^mRX zFj0!&bn2wb1W;iBr~#(qEto6wRX_c80`f7qOD_cB_fI$9Ws)GZ_~)BHw{Rb#v}#&PL#H+LiLjf2~lZ5 z&7~ck($4<5=jb~!LWi~EZ}5Vj+EY*1E-9g!KCZ+zF{%rBEXc6SFrc$KZA~Nv#4>KV z%9`CVW@m+oP9a4Y-f+puB#~vq56~xw3Fk7RhOa&7-|%DkHokX$t1|sXycTv8=s;p; z*!&AB=4g9$1<+&9xGJKC-80rMcy_`_o0mgwgrW@`o8FF$0x{WYo?vQDL1_ofS`2G` z4q(NgmXHiGx4?iGb6bG1`oT%N1J!CGy_{3Xom4EpDnRBgIArpRbSsjgDRj$;7iOlQ zE`+b~W)bF#EQ1GrTF7lkc5@%sPUs<)eNzNw2FT}T1ZE|caszi!lQFIoZ?!<*ynhss zFpq4Lk$?stxl)tGj!PXH^D0UyYy;)iFPZg#B9eBt4i(p8Y$eZ{S7FcVa(dePxWd zAw$YFCjBm$DwY3pKvVTns~)CXRzGN20Sv+sF!hWE#tBc(gu)YhecJ0=v=evvtQl&L z&);~wreSQLM=p+-mOc_B!SS&%(JH(08e}SdFk4>5v4i9C`HtwN*m>HoTgO=81y5_`VObL!s} z3T_iz38#0Mo&qMP)dWxh8OOTp)b((KhAsekRA?^kGDgAoZ(YGPsU_Tv-9V$Lg`z;) zE=(mwnO^+LU^(O6vB8a%wNVH|w2(i$v!!)wDf-!fvC{)DQ|^o~QfRK*~TYtX5Su2h6} zVX$ELvvtchP_EgdL%aE?U~f09%rVEL9J2P48p_T%5erC9a87sC=GC~QQ!*D!9BlS~ zadsB4kpxM)HZwCbH>Q;4? zO7VAQMuZ~2z>4Hsn@P^)!H8nT3?3u7_$#Io;sjGww{hg~p<}Y<01crI3U*=W@gsDew%^>n6Qk(jl}@Ll8l36S%|U;#04m@VuR+a+0mG<~73v%duD(}V ze4T+aSu~9Dm+=Ql;Mh_Et=gpt?n&&V9NWV;tyHKfyzB)xkTriFJ1%v(T$JRnwucCk zXHo1islZ&WIiDXXT$RDxoT8p(WqWKc}}NhRr?ny9a?Euqrk#%J#NLgoTlwDv@wkNs6=7zc3HBSN_w@yvmknNc0 zNVeK-TEbpWbH}b+>fXYfm@)-35mycdp7;+;*2C(-l97%PGA6?CU6@ZN`~~=Vm9}$IMPu zX}pF;v&fyL*84S+_+qwTJ!1?JJqJouGh&RL2fSJERrkAB0lF)&7vu{9`1h($gwC(D zd~rrL#tVabL=+7I2TX`%Iv4Oy6uVnSvA#c9O6%OP@aBovZ<6}OFt_?; zCFoYHz0Dy&F6s4o#-*PgL7#FHI;Z)Ka@;}FA@Q4Ia??GZp)b|>Yl&r!=5dpJmZoMG zvfUaQJuh(t%Z_1{j`io*&0xZJfY;gfV!e%VdDTs~TMcMr^0|F$CXbX?<(qn$l0J-Z zBQb6`)^{Fhcua70^6CVv1J%neYHaLV$I@(W=#pU9cW9Dmn|hOI)NKMuY2=&W5lTA% zHa8GSZjJB=WgQtdH+V^I&G1>3jvRrzPd&~MeO?qmf6F?WFN^4uJlbq-n3Mk2!)KM= zeKz$DmwV^f*hh^)u0k-Xzc@7YHqsCp;daO!_FD(0lzF$<+=wN`)WAQL^L{rH082Xl z{+Z$n)%uSAnD{ybW|?<|&5dwUOfx)#!pqO~9iyc0r(sZfQCZ(987p3g*niEk#Jy1T z2(YOsy5nl!`)Ec;ct@YMc5+i zCn*@)pUF3HAX$vaOXXNN*1wEGGZ|=)VL`=AFJFzC-p{Y9GF)K z%s}y1FL_S{+p!3elLm@r7{i9{nQeOK1LowmHD3>zhh`T@2^iNt4G&?9NzvQ4zqz-; zvNvs6Cq20$XF5$9T?IDS1<&$>ntmpLE?R(aMMM(GjHdn0^KgGu!udk(PcaFlo{ z*18+D$(6zllB5TI(FTAhAY@AbMdNRn<6h+-@fBJTbb4^EPm}dT%6c&XbFyE8uiJA= z9Wka$F2!`1wYDtD;)Iq z+YmK=2Do}6x-U@ezw>u0qg6ued%ZJ(IGQf~pnGa5miSc0uE7*W7P@3h%6-Ul`23=^AxOc1D&k$(@_zFq?y9Q_5f zE(O^_N+s+^m7fz9^io~L!Yr~-O;pW97~vtm)-+9aWkyJhA$2XJ)>L+psG$>umV^o@ zdIX2TNa%?nvS~cYdIT|q9;Ez*NJIFcU2rIy?Sy>qU0SXt|hN^Fi+2QtH9Ixvz1iO?cQ+>&jsTn7G3=Cs>FHxm7dH%Q!4 z8CG(%NvX?{!VZVY&D0CKX~YtZ%N9gVD#JD-SKxv~g&OReh@lmArA!V|=78$=wFYwEm-Txq zf!O06C7}B6XQq5>2K?~-MsED2t4A%SZ4|4v{<&5GUe&Yr@FePjVeCw{OD!w>kv)V<|v$qww_$1PiaDLA0k zW(UY=MhTq!{b_7isYWVo{PdF=U3*o3nN7fur^GOD zNz3#SQ;y=FPpAKF zS<{y`upO##?SFh<7jUm2J#bH2xB-W2lxwe?lZXzbjL9PzSm3r@4{? z(#wzVLiLAKHT*(zgqA!Fd^Pl(07ye))_kyxkdtLUO%oj}>b8#%E&tM!2kLan_Rt~Q zYl00|B94O7Y1oWj@MH&j6uYj~1?pU;{**jcZMo~rCCURuF#Jk&;JUpQ?QAZcvG5E1vWGyj@#Anw%{^g@BRN~c7* z6|Zq%Tr<;`+VsXgHVqWm0sM4`ksrH5Uw?l+dS+W_CRjKF_N$;!x^(GAL1;HQZe87prk2u(^V@UL8)i&MGie(XYfA)Ah z$JtRsC_yow^aXTsBeFC1%=adLBM-cl+ib~}ah{-shH>CJhAn?;2LMzZR;UBqorn=V z$eSm5^pobO(f9D7^S@7yd)}+0T_nowO@_`6oYzgZ3no#^twy;I_h{^0+B%g*U9%Lf zQ+px(<;0TT=sdZfx_odv6T^h_4@g{E4xtr=JiNog#06QDTh&LH=_bv(1__x6pVTUs zChi?*2z47(H>h4UHY>*!@*L9lV-MFHP%V>$n(3H_7bGHRr_bZCthFCA6Pk&@bqGb+ zJf8yXn2w5b;Cw+^>jM2N%bwncBh*!Ks*m8LnO6bk7xpCavBADGbWg8Xmj{CeDWvis zELO$YjaGD2yT-f6WOMKPA}kLa$d_L*<&&>MhWUw8->!m2(jc6ts>S!3#<2r!<(w-@ zh7;}an6fHvUUhZQXiZCAk)%v}uf-(mIy!gJjza@yGt&p26?PPlZl9r?JTCR!5GG}8A5l!FPRh?sat}oA zoU@5RB7tooK8MDttp@W1y;$vU4BdA`z&-<9LgU~VFVed@MEU$6gr=K8WGv?n;HUiz z^X z@|wE7M3atlhQ;gl!#$7Awqy5tSP`}F$(a~DvUmv_b31@mt?bpK+q)!eb!B^q8S}~w zxZ4ei`Z0gsp%n9~@A0!pu7~wScDCSA!X#7~#0FCvoJlYNiaP zj>4HWoxj}#G445yW*pMXc_eh8UkVM>>yg%8+Kb0@=xzz+@I00iqg0A~Ey&oq)Ul97 zC^c*0k>jC1j3$Th>EUg7jq3cNzc&7nLxcwV{JTF7d+6Dmb=nmI{m*$U;t#BjhB?B~ zo-bbPLn^iaxTppNTQLUSXRd7>1C;rtWLnTs zkY9rwHO$={oEJCY)_yQLJ@(07;VWl29MAe?dWT^q`9D z;s^}?!}>+l*7DMC{6hEwQ~KR8D3Xj}&99Z4M`x9Nk-=3;98WiQF*yBro*u-L(~gan zyN%UUQ!Bz(0_2Tidx)^GSM=qusf;&rLl-Nz%BH*A+l+VvX=tc^OF4~>Zvrqc~$(E8AFDX#M z`4F=uY;l0z)(fTF)(}TrUFPgULw#UOaLMywwhWFZMN>z$Ia-qe{-Of@7sD>W7T+@w zP^QHX4M*LXDTP?ryeXf5%B--c|no?{MTZvDwfnI{{PRi8J_s@r{kZ4|i) z#;(hKj^DFtQjy8lv?26i#X7)+G~cITGr+bhYLzshE`SV@RwzzPH>6hz*fZL4f{@dk* z{Eq^^dxUc?GhS5chiYUf8>-ov%V9@*VYL$o zG0kv|xmPJJIn?N}Gqa3!?>`WikCpIIbs<8%G9L&~IBuKD%yi~rg_QA%MV3PIl#AZZ zq8TCWZE!x6$K>5o<2M6&Nx3&fp><28nLo1vZsK`njwPo2ZKX&WTb4z|`8>kANlFAr ze)iHry|A{SVMNI|MC!R{|I*=KRsGul;B7SHt^9HD2$t;C=Npj`K1Kdo5!)W~R;~FE zg3#vKI8-Q(j0J{A1{5ji#tf}e5I;J&AmaRkNhTkcohi{d^bbLiup6HYXfwf+f0pM8 ztmnBQp~P~3*9@YFoZFG%$VrPdB1?@LOib|pgZ&S<+5X%gGKegOEd*wUkxT#3_U*Z4gfIoka0$!BaaSK11@sQ|~=X zc9b9Jl58VHR~} zHUqZKnsgxzS@!WBBMx?TQkd_kFM#n^kaE3i->IxUMe^fcF-}52XN@yt<`z)OLcgi3 zJ$TWVF{BP|==r>DWIw$_OFnXw+2Ah0&nol&M6Vx~ z@_6Y~J(+$+Q=|~<`HwePLj)W~@(ca^0~dh)dF>qewR)bNL9u7VA{C=2Tlc_zSFmnT zgWqgD?TvEbxcv-`PeT>wIBU$#l-$ALR+Na$>5g!-Up zh`MhL(^y&;E}Nf-UqLiwhI`?(X{|>}%X~aCiLv`|VL-W4ok2f)YC06uNTh}bJ=>!; zOI4o$##){Surj@OLt*8QxN7?xe;FX4fvUWc zK$pfh*;aq7Dewe>JE3RFb-~7-*4cYh_jd*4iHX+i%Xoa{xFQ|j0JBEqLhFgAJppum z9Q%3<*z2H=-(`RJ7L7lZu)p{QxFa=Y0Q}GZ*lANQ^c({dEz`T8*Rei_-(;5)FsS)Q z&Uw*KUMSq5e%?a$^7pvdg@O6tYO6TSkk*;VD9X+3OlV9nus1Kde}PGTG+h{;qo0cFyL%Z-De$%{Q?u+N$?u^ zC=iWS;&@DB>=ag5xnEMGZ^LFMrY5?i@2w2Xu)HMGZE`2;+2(g^^s%ftOu=3(tw=}!7PEw80 zy!BkNFk@>PTMN{beY`MaIp0mD>nx8ky4q@z{OT(hUZ>y-mwa2J;0uGnWwc_BF&#uQ=Dhd_OCOr2KmQ__Rs@4^v&|H=6$8w~TVu_oAS zM@|poaRbt;6@u{&#yvy5FQo&<_h^wWJXC{Qf4I&gNcNaXt&ihJ)N-6PhrH)Y8XuBLmj4SsQCV8H;mj z?mV2XS*lS;sadL72%lC`#_wA_wCytW+2NoEwti!03n^(-hz5Xp1AuUyfxku9+oIjQ zh(O*Z-2C*mivHG`Z>y6$Yn{QbV0G%<^)Q-gemHFxj3sjc=6L8Cjm0M&d+JBOrWxlt z+;RFYFCQ6SALh_Y-Xu~^SVlI|u{1|NQ<%op5iRcXFOX`c(aM?u!Act;54yx=@FbDn zL8g~S=y+;aagAaov;{b~BrNljAGUfn7>dSm!~h-hIAlPi<^cqtqJE@X^Pm*NqfIo+ zGR|D{fD6MTD?{HP9<%j@oWdh5L*Fo7UF%?x;`C^l{SKG<(2S#d5erfCK>+mMhgUxNSWC??|| z0i$)BB-{1gT=vcb@ZK!ib#U~>G^)P&?vmg=Ke^35>PCO(19+by`8q$!PCY2bcxe^& zwTy$LyTb*%Pm%c5jAYZ@VRC#;k|DOf5K?%o(5DK)>|#VnmSLn>$J@>hVoA;}(zm6h zZPL%##PQMBHH#Ky9H^ygmrPqo%LxE3?BYP_9b-ia%;Q8hj8Y{L+yKZFY5N!+6kKEU zVi^Y-06e=mRC>ocQMtyEQw*+o64(u+Xh{OQxchHbjVOlZ0SSP&mn22wfEh5b%fL3e zh|xMkvaD4wOwx%_s%a7|`buB7M*mBO*(C0qj@c-V39w-h=VE)yk-)o1FUK*;vbjf> zz&lJb?F6eF5C+)jQy?l395ihM=|rBdwg=G4jx1t$REpX*jhxfpi2*uRN$eU&*6Hu$ z02{yJ{%RcfV7&N?@*iLn86oq~*KN_`$T&7@v!)&N+uoK)@*iOo!63_P9r!l8uaRXp zjvUk9ZQI_)OMVX1>#>b4Z|=z^c(0Rey8!;m-ib6nS4;ZtVPvNt9NXR&N)p%u?40W) zz17!PJh-(cV0x8aF8jV zo8rXt#a?JKdYKQJ6241sBa~@SPN5w4Q8;9+(AoCfh}|Mx{p1M$vnP3r79tO59`wTc zBvU;Nk{xqC^ghf>_E|Q(~Z$Ni@QtsPVpdd9kl+eYtOh1!}GjZ6S@uhwfDD=<(=O>O#sU7 zHgt!y3$$y8XP1&7RN&dR=!Rk`NnpbBPF%yUD+PWxrWWs$6L&n@K>3BAJ@ndK^xd*4 zQlNk7=6EL1Cu{KrQ!}!AK<==*#_75I0_78dNN{=qqgTK4!K59rcOq zE0S+oz`Xj7akc#&=q<=^65+;fMgHCE3Gyp|_Fa4f`g6eRPT$>ccSkc1mXNXSPLm{< z5yR3u>ZF%e)UHeK;n+MJ_+5%AXqzPD{_Q753;_9^DQ+?wmFwLlZd?GxkDC|ZL81CV6YNQHiAgg(|cYLrhU5zRU2Vuu_ZsQ$)yK8bOzKfS4RZ|(>mN}NW%W~;^=X^h#0t1E}^>RdW z()s|;80emEp<+idFT<9)9@3ZU7TT9GKSnm~c35>{;GSSSNtc`y`Zn1k>NW*D`ZjqK z$~L7evH%%AqputdayMBeayNx)#CAN%p23{up28gX-rs_9GWj;mu>!9`)Mbm5g3h_5 zhl-2zhjbaqR|Qw;SE(Y350dtBouX1*Q~Ac2i$VfQukzNq7;eR)@_JK!m$-}Ss(9=& ziWp_9LxV><$5hX%38m^-t}|I?^-l4l()I(1M@23vAF-qI>ap1~NXN+6LcH=%x?!$V zyleYW=;$rSynPL>(Ku2{9*U9EXb`S`MLJl#LdU9p%3~hkx|v!@46do~9hoWPsCdic zD8ssrX~yk3r_aSYmn)3&&K-2i+*?Ve(bEM-&q8Y4pNWg3qs*xrA|dgRV`~R=tqdL& zaFo-raRLc^8MD}_-Kg4`Yb;^r`9Ni)!;o&<(YmHuXGXyELiIng4)~m$9oHD2fN5O~}k?t03 z|KZ0EIes9PLy?tx8wDCSgKD=vZtT!{6VR#7K%am^g=>g(N}XRzP>ycRjhUF_ip7Tn z`!_lmD5#8VsTzFDTuhw~LGGz`(c|QMhOKlA>er^%7{?b$RurSotO~~RFgmh^8Xn!&j6SE#hI8`IRn|wZ~*aae=O7h{q zB*c$$Ny&?CLjiJZj6CaVr4#oP>+!A6chx;;V6EoFV>iEr@bwXP*Z3eAzIcpx3 zbM+XQg&Sh#4FU+3MIspo*p+k;nSA*!=Jq{TjAAhZJh3#<(1*p74r#p9ldOOc0>>pl zY02^9GnD;H>yme$%|2nq>|&iY{JaChk$p~5=I8NWs9Fy7GN(VN7UlOd`9tU4NhVSl zdxc-P>Ei7A9ADUf9`M+=d1ErfU-uSbbxZkzWgHGujb`_6-bt@eBO26qhqS!+W{!~< zFt3qp>SvgTs^i_J`TIUFF3+cV>)7UiKDTU0m|whV`GY!W78Bl#%MOj++IrB?|7s#< zkk5DOZsGk1Gi~++tAk|#jI+dcD}MgD44PlhjF+7qHpM8|Yk7z9IUpX1_3QcZLU}OO z|3eKpHP4<(fedZGhEW;MK5s9mWbgNU5*nwvWH2C!wMEB4gn2XE?&$-o^H_syX-)1X zN$)vhEsx3Vcb-70SEpo_c0|=iu&swfU`K`hPn}O=Sh}3VmJUyFv1*Y}B|W?p4m8v2 zzy{=~yQV!nnJ(I5#jScDS~xo4fbMO2Le{LnmP7XY0I2kpKS2z?65$8AVj>e-5Ikx4 z6ba#{PfHKvrN-pszuAy2-X;Rh92T!0bPUo%RI>;yG#VK}x z(+PgSFy-ASknvwn47X2V#wJiP>v1)Oxlkw)PGE-DQ!DDX8k5QFVb!5pQep8aJN$f7 z<|Fj;$^kz0_ip2>3nEw?R{dT;^}X}QeAV_v z^xg^Q1)VR082BrMU~%O7hNFB@Btwp1XW&p?WujX4&cHI^y_1!vN-&TFC$Ye&enHa0 zA?aXFa*SU>GUXKJu5!im*-FNljhea;UW!Ah;4~gadG3jZkD^q&mu)oeDvXv-@w`ma zp>SHSWKAthORnF9vZ_0<6*br*g`Qlu&gcUzk0+n4De}Yg$|J=?k z{z%WoT=O{r)l(r)#?=wtbtRiI8VNqSjt`+xMeEJeH|~NBKKWZyxse_ zeW^m2D%G-G4013;YUIp)t(C%xz)dNvV{Qm}nsdYKx?U9!ec8K^QF^5UU+yMj@i1_mISDFQ+ ziMlfc+-U??`|0NzXU3Pg^CvF?43p7?6pf)(IqG1E0e=!bHN7mTJ4O8goyMj5&7oDf znD7de&Ox6m1ZTN!$O2PMW{X7Bu8w8%non8UI znn6`YZ4o9cR^>z|61`ONo4w@HlRV#$msd(Dqe+jL?V_G) z-b2I>C3bIY2uBFA|6-8Z!}1-p<4Jj7RP1rpgqZhB@^U2U=~`aRBh~3+x!T;j3a=aP z^edmzrvzz06wbjiP0}5w(Md-?OK7I$cSp>gf|?$>Pw>1d9~h7Jn(B)XKEcBoZ1*8O z;YS*5_hUVQ$`}aN zNppcIJEOD-LNjRbIrB_EamK(8pp}j^@W8+k!B&^bu))H~R1JX_=?`|a--FUsP)bis zsEJI#MVrp&e1vU2m(>Czr(YU|-b11z5eGvPn30CUYqn%bVs60nPnc7_6#{m2Xuy7z zmayOesuQg1CDPDeBK(3Y>qt|gXi})x*#7!AaFLFFolW`o&TPK#jxPU`4QJmC@@(vE z84dsMOX$B!+$+_zm96GNfH5rAoN9Z!kzei)IlM5 zoLoXXtJvAzuI?9s{eeCf__z>hx6JwTY5Z^I4?;R#$7n^N3gOIfZ?0XBn@i2ze~$gW zo>Bc_HBc?&Q(+ZJP06gsFFIBhk^e9y=ZC8-jgZl?B#r%i@#&QjcX?bc7D5AP&hDTi ziM%g^bBjlS$cu*>gN%y?wuv7-n)0R3&DYPi*pScH*X}u240u|!H1MR!t?_|u8La;V zIxH#iNAwMdHo~7%&N}o*QtvbdMT4*h^9bevln`FJAnyoD*oWiva04vs#`uvtJ%$l|!5kkE{2bMXO zj__h~B1_^UBK;BlQLxfbJ+< zGk!T=O)n-=(1VvNW9hPD5bwDlwWl`DWNl;{u>R^CL7C*BEzfIk%J~-2lGcbaq`+*t z!~6LBY#hWG<&Di{-g=({=c04pd)P9O0M(%byqGHsCcIvfzA;c+T;h>eqi>%`5O5uf zD4Thj7x*3_VQFZAOYr+lOjsa?A{15I0u3w5dbgs7d#%L+{TbZGc-KigY{ZlY^Z2kY zrGF5Q!S3Q6*|(xYdshQ;8sV%|Cs2GpA*6ez3eQpydKe>QCW7ee_4x<<4HN2}kbUME z`4nx^&n%%gg6n9h;=VY%*DK!D#-6b8#33zoLCj(41<&BOa;bGTacvf0=4b!8_rF+@bI zgN^}o?MBcxxv+O7Oy3a4BWKW0n@IT(4hOH8M$~w=QXymimEE6>IFpyAzoPcVO_uT- zvX11klDSOxr_NL4dgbihAl>lwbxOQqbV{m|qGIq*DMGs)L%XLp_qOd95DUDDfHo1i zaA?XQiH0`At~sZSt_%h`aBma$K?@MX+&99qx$2rZ!yF(T*zsO5iu=0z>Z213`$~wi zIh^p*?Lhgj^L0VScRo;~wzfLfXjCb>&@22O27Ms<#J(wnx(Lk7^e*X7lH&9mKlyw(2x@(tSpT8X>{UlLEat+72*r2VteQs^SWSrikJZ>Bc= zb6#?OB8ZDXo}y1{S3Ur#WzkItcd@T184H0Br%iDCsjIH<;ezVth*k*pR=K9v3`I=~(B#}O9j=xI51;U!AR z4F_Siu(kSZO60GisvI?gsajxQyX}cuSHzBz3;?(WRZWcw3OE4#EW|}R9U`h_D`P}M zi=+%T=)JSEo|HXh=&V<1phI4s+g3p(0Y1gzV{Qu%7}s zeibBQNg;q>=hy8|aOC8AcBmHeZ1w^ibX)5?0|H(r$pH++GT6>a5RP{iaQ@1L246iX z7G8Jq!jx8rHcbgl1YKy-ZV}z9CTe)4Tln^Elb(Ic@9i>GQSLprJY)9L!8 zFd#f?-lVTe!Qvflr3SG}BMS$ej%t#pPUbbn6U)b$z%{qzm=GDp$?~!GsgvRr0X$tl zCFj{((HY0^zvdQxqX*#2nMHzBsTVMS&{XZJ227d~a;SC$ zG>@5DcK>z*1BsdZJkdPd;Jz+viN@_PF&k0pmA!(eX|}J%U)XDdB$RmC9u5T=|In3R znjBVw&4${bBR(j0Rg|!89?w5=!d;o?D=KCo32>1#(7y{hlNu^HPvnX9?V`gSJBoDqG-7gA~ry5I6b6|3H;zP0t9`ZU&_ammZy zu}W;8(W%z5vTxCI`H2j;%I&@E3Pty*cnwessJ4;E+@^ofk)Ou!-3-3OD*uC(acSK% z*8_$_DikuBKYDdE`z}GYnAIHjW}kphyz>((w~wF{o^^$2iDRR`o}5=Z+>4g}9y>p* zyKiW_ro+G6(8KcGW?)-xntgBE&|Lp?U}a#1SxlBFW-?$$w*f-ft=7iOU)S3$c5c&o zvmaS%6xv``*Ki%}8G4<>rfPe&3BqY*^(aeFykP6k@RjHC$8D2T*Eua^S~U!9Dxo*d zr7J?$ZacyruwUD%X%9IP40kc{ig@$K#b3uMU|gY;z#YVBX-3j#TyEQZPmoRI>n`3W zd59LM>Bd&!txq0<*W1{?Blrt)I}!z3N&U6Fa&yjO?0?dk{}5#Nq>X@0iQRExju0pJ zhy4{sxJBaO(itP=dqHvn)$ud#1+9wIt`&6)lNTWV5-mXS@&`cT6+J?g67FEW5CH^2 zR5_z5GIPA;92H3_u2?@_=yw(#Vw9D$Cx3O5x!_86#`llszU@QeM;H5!U4d1J(%iXU z5^NdwI7t&+%f|nHd#sYoQwXy{UbbdJX06ke2Gd@p(HODAvA2%e{7#qDZA$&z zL>%8lSO%3VHK>FU`y*OMpn$3biU==mW8Ao~NDA8;4FWPX%Q--$_EZFbG!_U4;v=GKO%V+T>}(w+FbZ{JQo zpq3u~T;vsr+T9-Vr}w+ETrpnNfKg6(6f2ps1OB?&Ie}qFWOho=vZUEtjZH*KlBZqd zU-wnf#^(*Nq8|4duT-cdvYb~K@v^uk$iva%%R%`@MZJ+}QP~cLmQ~rkorr*_{rbe(m#T?51S0H*(VuS#+w&A|k>r{4NRl zE@pVScNX<9HIeseYV6JP{YIN5&=_2=+Rj z;4)85d;7e7h1kJr4=TcU4^f$m?%z2%Xt3lR4L2A0p(6%a8*w0PLqs=0xd)cWvyzj) z<#Sl%0F2ta)g|h`iX$AccrElT4#j^aj`2UmiTmf^moWn>;*7>Aq&Ef^U9`b@m4}Wp(+4y&XjWr%Dju$3c#(3s z7FjAQqAAY|dC}`bX?E?I79B2_{{-)Kd?XN@ft?{oLl&A0r}6G_?(c-yt4u|5q6#Mx zJ*Fk+1T6`i%&g8#4DLi_U44A&rr0ak-nuYyA}qU~6;E4_SA2~`MFG@M(l_u|5WO{^%;31#b0x_) zf+o4J8fw{~!U+C6*jqHXHefQ3wfo=rhrvawPPqTMz5mH-3Xc2#*_#uhC3A`nPsV@EyEv z?BC`-{yzTKsWjpL`g8llelG_?Xd&CTEbIbxbp`DgLrWGU4HMUT?kG-BD@CY?^@C&Cy$_dH z6*CFlmNj@BxIwA6lxwiA^3dbZPB^*>Z3&`&gKGPC#Wut)4-6l2Oby%ULu{!eKbhcG z%LL+eR!=-A{0A_;c;Z=BP@0#f@pV*uMbhbYL=>O&# z28vAiM1ukW#ee5E{?`+&5#vA8x_?yH?a)lozI=*V@Y^QV9I5NpFTtr@dFD-N8VuHK zP|M@hnjUgxjXj8_uH>2m=XLA;py(egehH!yq*L&#EDWHyiAsY!=Y@wOVzmyyfuFn2E*V=dMwyY{%a8Ksz9){XW=-c#w~hgesm z776Niyg6WGctro5(DiFq~H!V^Tbs2M7(_P_3sD-7@0W4XzxhO+NBq9 z>I}`x=$blbNMZAdLC=a}(HV_}C#)DwNL-@7NG|0`DF9p9%q^Qa>Oh*~QmEH4G*BSf zNI(oFUO*R-)737zqU@u*D&z%K0h8xR^XEfW z?Yfcz^Ty6cQPAw$atZqa;f>dngbrzsq)01fFcdKuURcdNd}GW;a9yA7z+S@<`y7Dv z8!fEb9r67jm{zsjlfBq0L7b7GU6CTpOORTTHRY8iGz{YJm2QZD<_2JML#9j_XEE%! zyrS97h<1qT8^(1Ft$!m1PngwCVAM`&7-l%0GrI;;9`LK?{DIbDCB-Bi8Wjt?56bo%F55CX(LNf6zq>v_CyCYv|Uarb1>+>Hn+R4GSIJ9 zQ4KEZF25^6kIAEeX+%W0jN($IV~V-(22v|?ya_X_;ZD0xazh)=FHP!Iub0^QOC zWV7C0qbYAz8_87u@CIJlc6!TM{`KyYCaT`;`YuDw*a41INzNE|{uV;XuS?3Ym{ikW zRr+sJQ&@V(JR_5!WjY||cs0P7{KN>M57dlLQ>mB(G2mGHvl>a5z|JM7z=k&_Nak=$>g!>6WEe<04 zN-zzFYE-NaG?7ROrD783^yN@wVQohHslaKRuBP!}Hxy9eqS-kTIy4x(K#0Md#!96y zHK`>ylabT6-pAZelb4HZ{vK%1PPQ8LL5v%oXjAlbQ(EhJmaV=b7ww8CZ*Lgg)xiOW z1NnXT9f|cnoC|K^Z|L>UDx!21oSY2Y=2MeHeEa0ws7r-Zwbcv+ zhpphsTx-g9qYCs--2rYvmEvcQF%&2~b9Jh=A82PTiP2&O`Lio=Bp=RLh$Iv((zyBr zXDMIV^LJvIQ!O&)zZEXaX3r*aef+w;T)M86gBGM(=%HHUF~b_gDoOI{ly5Z?Rv%nJ zi?_m_HMik#<7n0R^bL|*(WY6m0^me^x!?bu8p7j(?~+QR(viC_PwsI!9+Cea(%!MV z(so1>;?v02ieuujv0HYvsr#ojM@4p9mBgCt%;QJ#ed~|;TUsov@any{L4D>9})8J zfl#N4{Fk1A@?ld~Lt5cy0GVTMU5g~pM+jaaUO)&Q8Ds`S0&eXTU%{XsKTW(`n|U06 z>2*;K|3YbvO3vhUF6ARyJa=qsCfVISVm3SwY+NL5a>;Xe(R%24m~pweqWkf2P4+Fm z1WL+?ie#f<+^SO}H6;yq$??F8$I?2j(W=ovoh9>9GO^BP`7;7r8Pd1$$&*=~`2wrW zIN|o?UMiN>fJt2RBJP@%)&RtCvZF)}YybpLP;VV00Hr@PqG!g3AhkY1Bl6U%x@s`_ z(AtymlwBrnttdmK7^8|whaByM3FJP93I3F!7+@JX z?Atuh&L0Hl8PM51s-LFsWe3`=vbx?i`a_sc?>cw6y8c1^xGnT0y`a?zGSJlQ14(m{ z%VMY31PqyN^4FvkUaESG>^mrev_(Lgj`KkZ00(@g;x(Kg+VYG)7_P{)KNEUxcwUga zc(fuU%-yQ;N(CCVSCa+{E-_4FTNne~?>h@QEN9c!v4+ydBA~X^Xd}f+3>)#tBowyW zaUpPgX)sFEBRF__pd=|t4tWoE_fbg5umpT>d)ofKMsH`hA_ z&IVDSDKWg)2=2>hjMi3iE8vlf=PS^P#gjm#6`CJU#ph#&H7w|Hq=^G7+Qe`LAQOl+ zg|xcP>`B^MS_>@ls{%+Muf z?KgGTd=>270~K~z*cYok-V)q=7ba_761^QQ_dwC!0^Awf z4-Rv!`?TCEh23+E>OVSqM%Jdh>-EA;79%ecgVxJNH-?*MAI&gPgW4uMWf>dQGEhF4 znfl1yv&ZQ}4T#+iURKWNXW7IPw+J^9fF?b3TGtyY5NEoyUG|rmoVl8E9qYfJ61Z5S_Z)*+sOX?~fx!SocWo>Rh>R!#F*+# zK4&Bb4L&PACj<>DH7AkY6th}TC`}>Vytv`|1OsT%*>Rx)zG?n!_9{WbC zf(ngBR`*V0vMPn*uo6xi+So)qk((ssK0Qw6u=1O8OTg2IblbAVEu0z^2;YLdH%>6zuf2HK(J3HLmuU1`-Cp=z9NyVQa%)qfRba#_g2N4RJ z@f(FvtH9f_!ttJ5InBH_M$-oqM+%}jn}P|c*}rb|rhPg;dsnUe(Bb=_SBoO!htmA= zp&Lued$fd5elkxMxNt;USG&_qihu?tU6JriDqrnQ_(962DBH+Oq(t9#vcsbx_z?L_ zWM)o`7`f=`c69&BnmRShIBNN1@g~VCYE4P7+?VC|S#^fKXmDz^cTUVpWwIgYyhk!5>4Qv~FxIs_Z zzR3l(G2O{SAqjo+*1)h{ReBdRjYX{1j#l1uSyY9UaUs@30SRCSl8Y#*`GpSK zU(kxba3*oj%I(Mpf3DnOR7&U&onap?=^e}KTfOt?1IhF4E>O&m8&)g}k`LW~yiSZO zwfTn9Q!GvUV@JypX|z4b#i)%L#$YpL)c1#Bu{n!Fp7DZ8zxv_kwu+m5#Olv3&&Wsg z`NV0h_rKr7zjmq$Fh!@QhIAr_qFYfjrNcKHRJXi&p`;u!IgEwtuV3{4xZ)qFSVNgi z6UX^7{0V3Ml$v@hHe*rE_looBJxBWP64OYvCbxY7%fQx=e7=hhX<#ZiK zFINP3@hY#VRWU&29S|mtJp7tITv=;4AA0&)*qzN3t{@VuusVaXGqY-4Y=1(^ATJ=f z-<0_fQe6I(_9?ssbkY!YSQ$l;RgP??hfmh0R=DYaj)!I z#yR6l>oHZ`@#l6iN+;j&6zU{z<+haklXB6T1yLev{EpxEURL7NeOOJWH$0~eQry>t z)Y+A~TicE&aPP5!_=}Pa^l_$|pE4wu7h}dQ7+kY+`Nd%Oz(-f&gCX=;67h3h1e%OdnOBe5?iKTt;Qa)di>e4fUUP8AL#E?2)Ar3xiER8VNh1qbxN3x+t55?k) z^xau)XW&YaOer@v9XGsotQ%ff=5Hl{gqOnqYv_e_$m+Td%X6012U zpWLjwlFwiYbJL!fLDWLmqW4QmVcM(M2p-*DYe5g1tJTJb5k**&-8TLy2S3DtWRGI4 zDKIJD!E*uKo|Z{u246*J0#FUq_B}wX*pR9T*E@XBQ^gQKd%2I_z93|mqqu^Nzj^{& zC%d|Ea82x~t_3P}CbbA7vv+S-HQK|pUjK^F^BBi*xa<##;W8@#wLWIhLc2vl1Ff6x zuZnrqIr2TwZvl~RDv2r9DvaUHousFPx~T0XYSGckp@8phmuHBAF>|c?>7h27fJxTU zu$Zta7URfih7CVwN)E!^QA9skl^6wDK_Zy8XT5uO#hf!Fk24I8OZDv`Cu=J)qF)(M zS596ADqq3RwdqUyyRP=sFVqRW&wW-WGFlJxVnD+LqdgIr`?C(b0|4a&9%n(b*6q|HV=XcY-GO!1HS;a^}p zXLv){BUke4L_MIr{%`axyB{IK4PNS3HB!8dc*?!9noK|8BtPiugeSL6M~rG=Or2WX z0ol>71BlTBpg_Ks)g4`2I50dIUS2=49Ts33p&d~E&NdanQE5Oa{HN@Q)RT2;{?kpZ z{&=>;E2@D&1+=BVF4c^fP=Hsyt47ojdM{g__s=FoPo^^$@eDO4p1r0aKI6ZJvYrGR zSxSMFXQ99L_6MTKx(5_h9S`X}7nde%?=W1=HdgakeH}~1lu+m>=LQX4Hd>g@-Z;Y{ zP@g_{GuU#U;l%{*qD8NmfKOX_D{5vw3~p_@NXyjAy5R&Ftf5BpfL|G)E5~?UyL8t>XK$C)lXD}WNK~5osb;WLaO|x{7K=nQ7m@I z21{@g6?VOf{ng!Tv=zj7j!7nD3}Y{gw^R$7#x;o^9qC+`1RE0-Syt0uKX~rt7w@BMi?&A6ywG%aT?`Rk?U65lb>MasJuLvc_MVuA z8D8MQ9ewDp ztp`2jm{_9Ltls4dyDqCBZ#tTROAWA3@l8-Zi$Qi;?HFVY@Y}HG&K;%a{o>~co3p&( zimQg`OIdr3j}5a8rip?cI<1Xp38Jr2H|BJd+OJt<(>gnE#`_E21(c21C6vyp-ocv6 zTqdP|3~Qk|n@ZKA3eqN`B5ltMR2M|bx!5O56Vu$$ea&df%{KU5GiFhFnB96ka&SbC z!YE*qEoz9*>qtrmGhi-yS3M9)kj>%wsw=rbw&abPYjECnYgdhOEBmt2DRj>07y!mT1pjEVO0w+lkX!B7NmE}>5)g%h-3d3R@W+IF>yP-XX!juom{ zc3rDx*6+ZbMUOD;55LtyCueM-Jm#(7Bw>)sU^zr6EpM=@w%`W~3)eSP0&?{W0ro-@ z_JS%kV?yd{qltOqJ1`amugN&jGMcM0T(A_$Lpxqw>--g*c)Acb6|^2djEZfX7j(bk zZo)~RL~|P0($;_VTj&ZLb%$SKP6xubZyf)q-}?7-=d0cNjbe)W>9s|R!N_HfKP^y( z(8DW$ORc~si2y~=upFbNkSk}Ixq;J1n!fRy2@9ladXcP2!&9&1-a7w@ump=7x{hJjnZ$pPhSn@zMP5gD1^c76#Cx&(kVm^AL_Ch>ht#A?~{h#+|WBy)!nklF&VUL^%)6DAVk(1L>e8>FKWzbV9^^EF5jaFscR!qs0 zca=%Yu1|9IwJh~$L078i<9&axfjToA7q{nS+OVcxtbu?*qNX29eM++>E->Y z{DY9f;V@fPg(^#;Ky9b)hcc`XJ7juJj}2$Toyr46sF9NMpy%k0Dn^VHqB2>pG)PT* zie}(c4(nDcKe3eML-e)%3rUmoQbV+adf47M8Q$H~E}CitRy`m$H zZTBbmajaQuW`v5_7_mOvfqFuh;A6yaxR>uu{#%|ENrC`^vAzF zxsY8{zEdqdTY8V$GilECT(F+mx-!~P9{&A9xQTym*emF*4W41S@%7yo-o7i!Bud|? z)Z2}#a?&DigkOt7*OQi2kB}%r1&4mrh9WbzQfe}`DiL}$a@mR|?WeP$k{yR^EFBgg zLr0>WcdLFgzWOytrHmO%1efyh{ONkJL?h;=oPUYg0S08fORDBnv2&J1Hzb@BOmEiV^16$_KPLi~jKzk)git9tGZ6vDj zZ|TwRb60q*06SM0#+Nz6 z(rCb3C&;Tf$sb|+*uz$7VDr`3!-D*UeFuYUJyDjz#zzIsNZXCa$QPU9b%ql@2rxiZij4TMq-^>i%t8X=X!Gn&CLK6@AE3;Lr&@tAMWQllw<-AuyICP7SM&uhg(l+czkHr?T!26 z?vg&wCp!2JhQ0LjkLX=?2PerJ=kf8KO;|z!ka#?ZJ5Df!e*5k~|1EXzTu`l7v#OIJ zn!l(f>(OQJ+J?6Z=me&hEDFTe14t(xEfZUXaqFx`J7uMYTE6Uzk9;}bTy&di^qMKB1@5JG1(d)hI*xI@aL1AC0 zxVi^18bCmY-#t3^bitwf{TZe{jWae`~`9)VP?=#Z!KHO~KlfT^?itY5aMH#Io9=}}> z+9iPbA)s#=E6vaA%K*5D2tK%I$EpPTP=GWGUO5M$Y_Gcnhh3HX+VMCdiRdCT*dICi zt--|Pq%`jg#2qUxUt4u++tlYKWZHF6@;7An{e|%-`2QBqDDBrPRKJAM+b{9#pJmT~ zZ-o>pN-84rBYbfBR;VW-pvcL~@&CU46A1(oh2)XAhd6`;Dz{3Lh&mUZ?R@}%Z z{ou&|QNXR1^)7EQ)uzlFH@0J=EIY-vq3bipbbJ1KNcSzNMnb1?< zGZ#;1r>NDtOZ!3RVkjx52q%h3cx;g6XVTYV&=cN%AHFz!(oLb?JbRNKQG-}h35Rw) zFTM@`+L_0VJIl;{*S#&}DOhf|3mFUkOJTUQ zrPMcfbgusSFs-SB8(jvEKMJ;Z1}&&o#8GR#DNj)Uo7NIOXyo%RsaNz%%=>4w{(F11 zQE|;?jt_xn&H7}>iH_JDCh!{#_*qjgA{zlVNCTlPftOeSm@SqIYU@48(<~hjocGTm zVXg{DDExM~D~Ji~*P|Z;Vx#l%3e&07)PoQv&lA?qHLGz)~g;j z)6VZwq&C>k-Uf=iAp)9vTXO2EpoVJ8Xu*9C!t8Y}!LI>JJs379d8g*&3^j25N@Fmc z4HG@mr>VZ?2~JW1#RYn<#?CM^xa@Tmhiz&TRifowINS!)T#m<}e05w}FXB-NX8-M`r2%~AKFoMMi;NbOQo{cJSq zP?DK}f3>Le2v%mAuy$^;phPAsmd-ju?*IoJ@Nr#*>jJ$?6%rP`%wqi;(K zG(X@ym`wF3ay&TS-AC>DnS{YgQ(Xxos;0fQw!BGk;$&Z_DTB|MbjcwjTI{~&=(PDd zKD~%aAEXqoYrg@s5&XrH9-xX~td_ibA;gad%9YcL!3vT;m4|cF#dh%K; z9Q}-^S+Xb$W8S4hDHUkS^kYB@Y5}y&>7$Rl^EiHJ^@EWrM;J<|oCf(}1TA-|q5;JF zod?CISp#)7)S95N){;k&-;gwGct ziFtn09Y8k75CTDh8iD`2y0XH=WoqK${PupBZ2PTA&Ul%5ql?r^|0S!vwo`GYZHJHP zJQh7I%W_RH^2}>#=4tN~OhHT!aLnV~!;;R$b1rNe{YD~$u_!phLXx4c2aB0OVE9&4 zvF6D>4|Pee6d_jt(MqFCGkjwSn3@Nlt+5JRg5D7$M(E`LXPku4NFykN_zA9#!9dOA zDc8?%89Y8P-=YKQgF3wMP{p82)%oxlU)i+gPV;+6k&e=R*$znsJ!zp&pws_EJI)?4 zxt~{7uunOt<+90h%E2Ab+I>_^zTd_e&4Z)vtm=wnGy3=IXxz$n(WQIvP&`JD+d+FHAy%Vp)T(Z^|`sYC9#t!2ooW?RzyQ3otDYa%BWck&+yQUwx5mo z9;)%~zc&6MN(bCszsTJG$7GuP&tCL@$kZ%$+TkRTRvc~ISHKsK=e&cko|P+^Cqu?X ziup#+VBZw0IUI#utnlft`|Uq$`hdZQ*cRaSyu=WKQDyqK$OSKhIBBwONi>{yLueV)*p&0gUQLXxxjrptvcygVf zff{V+Dni2y-}9Mmz}ySoU+r)gPT`v+b94YW4@!+pE;7j+YbRs>?H%M*2@$d9L0}lU z0Ye1P5860Kx;!P|#F7c1u)2jjT;T^v%h(|qyvq(qjnBO{Abq3BOtR2?xl;T&_W5f? zp;Co9c+wR?_#PoiW(YMX^|#61{<}*@(KcmOaHWp+5QyNsV_BBvs&vr2?iDEAJ}29m z$1F=_ke%_mO3l%dh-Wlb)-G=ML)4l9S6I7JXUE1*XMcih+(GO@DEwn)b0g1r>i)po z{L#M*f2D!Su978cLp0!SX7hTI^6RD{`>y`YJU|7Aa=`(9?YDH-4YE!A4mNpiCHpxt zwSk|4MoZ?95@KxD^2bdleU;Z=j)krZyF)<5O}I0)g|90Nr)>pP%EhOc22%}S(DRCj z)0KP@#BW2OVwAoGMfqE)LPSv%tIC8lOVQsHRtlMj>spzJ>z|nzrx+eX-vDdOvM~X7 zNeAS#@&S0IKVolNaSy1T&nS>C8IH&HevlI@;34XhH5dYn)0D-w7+yh6ai(%7TB4NY8qZ zXT97{qI0aEJ0uS{|{%=|KL3KRhpsjDE$IW-)3JQM zI8vu0nAHOFTm@B9>MxvYQ`qzWRvP{oE}j3}M*CwOyQ=iB+ALbnw?&2;Av9nQyw2pp z@F2_4^gzew_4%3Vj}y0v8dF9RDYd?9Ref8n`ZVT_8dF8p5qg!OI!@%GW>n?%hw9^D z(Z6us8~zW@6#v2*>3?v>ncfC4ec|l&|Ae!?Mj+#~iISASeWN-A#rb8Zp$tD*GWup= zzdd%~_{g*?1M)}Gq(q~&*Aqqf{%5vTsR~t+pec7qo;#!@*)CE5IjP_Y-IH&!Lnht>&vBZHU<=~~x!R2-k#`U5l)bEu2bpy(@*q!{ z&ZZ4advAkt1sP5};aT55Y$44%tGa-gy&WU1U_e6h` zoS(5ntuC-ftUfZvVa9z3fAzynAQKa;FpJu!626J?glPS}uaO%uor-ad7S|>H#YYhP z5(3Fph+{}TS0V5c)ryz09B*GAT{aC}(2Sm}b@uS6O#smrh2N~YN1$Wn(>pr`vo5`P zslRe~oXe--;G{gc6c5RV2y0v~5hgU6zZrmHROSJU)G2qS-7tmZkDA zsus{!y8%;GVi@hYXjOwgSqOwc0Q-sigHzb{yrN^#Ic0}Ci}TVciWg!02PI5DeHv07I1(q_ zqgri6oE}f)1_Av>)VxX#>OC<08N7$!QYL zIQ~kg=D(+e0VoEH;bQ<_uWY!eH3FSj1kFNHfO(pUl&;9;sRaP;I4b)Hk<*mY&5>aosM`%Qw&IOV~F3T zWkCO!6bn!V*w31TrWdCxFy_)=Ba34b1&sJ?`4@0qh;uu^J$#zUqkMQT=|$m9+2gqlsE+o*K>bhCnAhuMNrxzs%#RJ zbquRanWbgOGrk}mhzStr0l`*f&|u_moJ3#F^a5LsR*xBGd*98{p5R!cAo4!Nez*Uq z{P-{6>vLsX>%V|!|Hr_a{}1q)N}96B{3suAFx1-6G%l!g_Y&q*7a6Kb*_SHSTQl~bx&|pLiE|=|zfDiy zt#yr{*T9nzX7WYi2Fs;tC2|hbhaoeiUd_`o;gvuxIz`^qHcYRryiGD}wePcNAqaAG zAe4Q-;=j#>!tpLKi&y!R6R?JI4b51^-S{U2RA^gcxZ0j4CS-ji!U(mPwrW9|Wk64=L0OB;bj3}X zqH|z81C$r=bACG&kIi1PA9lT}xm8sOfC%wnU$h>#zxR_eDU(H#k`VLv3eCABUAUCF zi6G&85cy*z`A=ywNpz0Vk|5s1T*Uy-HF@+n$B3K*DHp-LXO&LiEu?d1PeI2#N-vs( z>iRT{{J}O;!1fAuNeW2=Lv}K&z!XK_YX8+Pt^wu&{Zy4r?$5#K7C_!^Z|XYk)_@07 zG;=^LpP3G|k-81OImdy>mtBZtI-&J#6rJ%tHc&;v%B4Un)~$;+Ow?4POpd~MJis^| z@>M$xI@D4B>(PAvE9A+Kqvui00Fo?Cx4@e) z(+gwgq#Q^=;u66v!9!eHSkdy9H~73-tOT|zD%;o?%;{mWbx+Z&C9{X^)8Q*hfzz4n zkXx6S(hJX_J%T>X;OS;K`gId-13jradm$uLo6tUY}V2=cyOiT$(C zZ}IOhVeYGCq(}d8)xB0_?mrBI!a8J5zpNocv7r$qAYdqrtQkBHMeFRpn?ge(U36y` z2?qaX09Ox5KnoN}0P_61EBU}77)9rw@Z5rr*JMv;oZ|QA(^uX4dwyO6kD3hsFsR`$pfu6>(RgeePrHJuLo60V>;1K= zqDQ$9--7A*@CveOy-CpL#F>L$18v>7OSq&Jl}E>*$0+-u5n`##Q0_H*M1ozXU_^!=tqAAMW5>fEEoJhcSd}6cb7M4O(ajB%7#sOTj^3e4p(!1 zx-yo#Ze=>pd(CF8^sU_fdy{o(+Up{=2R>gwv9WCGzCdf?J?Zd z>v2AJnfRQ|ws5C#2RuJtLN}(NV2t}`bstt)I&US)Q#+8VoEu+yVa_*LoaJ@*^{bSAmNua_hT~_<`MS@xB|G>{`J%? z9GO;hh=z1hG3$Apqc=lzkmdHU0x*gCkb8&GY4uRNo3il@dn^Sv-*^0~WqZo8&v)%9 zGM|0*n6JMRGWDG!HtE2BZV%x{V)Py|Bm96W7_x~Q$og~gGfq27RO^bpZ#d3pm<-$i z`N0=uZUZOdg83$7WIc1BilNM(s(3c_G9=p0M*{;2UR#G3WrN}BzmT2$YqM+jg)Hqq zt_7_AFS3erU&zY%4h@0G3n56#Bb>u3P;HZdgEgK=#RfC7^A;T=D$WH#nRmXydeZ)Zg{sZ-KN=-sxmM$b=ufM| z0(2mf3?WS>%8qFn?gn8N5Li>Uf?tx?g z$gdSdWy@lJVCpopuVlf6!muaCxOl;Zt#XnT@to^^AnvVTOWRm@Q-t1SpJi0iBE1h6 z&3?K!NM-D-)5rKej;CJgES#G#yHXoq31TjSzoB>3X`Ia7ueVuoqtX-flVCNn9%AJ-JXZD6uC(^y`?YAPb^)(KoFBu>4*Hps@K==6E4OU+mQ~qO&t^b3u_PpBPCBpJN6co7=(QZ9U@V8}Xq(%#cyhI-L2eu5hRyd5l? zVP;+{v9*ZCY^<}pC&|>ORYQ&9hfvXjJgjWnr(pmP$6?9aVawA8K;mEm0)^-uP$#D2 z-`~)SJiNe%D={`Z(TbKIsEa2V#$#Z^1{nmnvl++9;`l>V?#QEd13*y8`~_9nWe^(% zV_xyL83qX1Bkd{WBX=U^wFcE+%AP-g`Y4JiNnMTrqSx8PChVo%6#Z>!e}u)7`x&9* zoE>jS0<9ilaC?cm75RqsFdE7SX^_~vf_nI*>?G` zow`v7N#=>fHm{aX|K-=f#^Bax`GqjeKStQ*KL~5e&hw#snD|y|hNIA}qsU#&UeNy5 z{!S+xQ&LVG&do>9>)gJ(+PD@sE9?_HRSvJmET8>OMQV}PWk#4qA&uWIn<(R8axl*E z)tIHuJ-`DP9|UxEeFLB% z%-0AblqJ0RGd@`)M*nJg6<;le(ShnvFAWyW2|(W+;9l`*tOncHgvZ@K&6O!_+DUh z%dCbQ{JXd}+H>??ZWta28KkqXCq(Qjk#bm!vR*W)K-`hqpGXAkD^$j=*aI&0H~gh+ z^r7mI9wjNgxHs_c6v20@myUky>PJ5w6z7ygIn+2`DV0QdbGPC` zG$k{2e3or#1+A~gq@bRq7F!QJxcjfy_`sW=1q@#VJN)DF*YvXn=gf95wg5#tGR&coc>C65pSWQio40m1wZ<%$7O>keCf5zH1z14 z{L~V^?^Gpkd{N|-4;#i3fs?k{^#LEU08NAN?lsEVT3B!n##k2Oeu7h<1EV8|*%ve5 zENGVlNJ^m0_y{m-$szK`ri1SLn*Jm7)@XKcR^-*Htg#k zJ!J)n`#o=kU=~3&7G$t!?YY0kLP&!q+Y1kqXpt01PSlbw{YIfrSK7CRWrM6QTvU&3 zzXbX8KWeGth3G0Zgu4xB4oytD2NOv5(9)6(g0Lywfo{pvA}s~N>380ZOUW$o;XeLe zAN2a!CTN!NsVB_Xb%bqa0%m!X!k2n=w+{UgTGAFLDzDGG{qgIeJ&u0Eft!OjtG&!6 zV8ogH?2&7S{$r{!GmET`zsIrj93DiI6*nPt$}qF~0j4t;yBj z0=pgEHYb^C;gTFs`)1pUtma!sQ6XaZ@Lvu@y{^cZ|1#HV{>QSv-G4aL{#PXBNe-k* zXlsWg_x|Gt`HE_qzcYq}#H_B%dh@53&*1lJrj>*VS3$!Yt(Q8Ibi~vL`EF)gD1fYvYrpIIk6VvP7B&A-A+LVq`|9e0HQ)VR!Qg6~^NWCn(wVBOWQ_5>fKY zA@PiJM2Y($`ET&`ye9O+@o`tV7e(&)M$JI{jOnLK6L>aA990h7GAk0=vrI6x9%(!l zYNVo5vhH`<*OKj@!2EjE1*e)-b^}tE@1?4MyqhdSF+LpeGa~F@kAMG7Ep>lFqm%$ zg^K^#rlK6KWJsd5f1N`e5b#w?#cyiq9$2w$9ByG7>UZ%`sS)9me3PVEf;?SL(OYb> zQKevW3!Nx`TUh12{griRL+{bMW!2c7&p7nZ4OcY1M^~P&y!YQimdnj=*_tm%Fkt@a z_{aX=DOW{X6-yZ1I|vG+E+B5UsKygDW|kQ2Cxx6;jD)$FwXcdlzQKZY9iQ3qwDjy8 zOy}%wVt3D&4obDVM0Ak9YYY|7un!Oqace~gl2|8zl#Gk%vLk!yb=>OXn>P8Y+i)~|@yXN9TMkXc+U1_PLy`OKx>c*--~Or%d8R zi6Pw4Seh$Ft0-nh+q|E7STL+bWhq@FacluWTdhqQtzB1tFEfTS+lgw94l^4q!E8SrEz^>G?z1!u2+#ep!o{Ro%FvvZlf8lHKjY=u`X!V}M;cms ze{!FFv|!8tun9{E=0){wXQ2IJftyKUcZSO617QxHX#LPZc7o1D)wurXH2u%$DBx^` zL+mVvXH|S5Km~9rU#QW06nXHFFi)yvWAw+w$9k=u>iIi(dgW-%mWgSHIajTKq5m(m2QEQa;3ABfX*q0avd40jr}hBE!4nze-Z z0Q&%Sd`JI2H`D`9{)o8$$_+%ccDrx*DL~7rrIy$>LfuoM+=-1hUex9%)c|ZBtF~<- zrfMiu^mU+7Uos%JOj9plid8|!efRK6Ff*ZO0mK4w0K3lNLY2AIlN+g&M2K3M{3i<;fzyAR_k^zF`lC<4i4Rw_4Fw^+(KES+mpbg_d=>@jX8aE ztRNsLfbyeV9ScOH5k zvH|99+6Na~ANrmI$ydjhKKI^RzZomfb17-=u<6fmU@>#MY=~dE-4&JD-h|n{H9GJd z@)nMJJJWTb+91^h$ZuBlcufk96`3>y}pWCwpeYXoo zkZ#CTiZo-W>0>(^!3ql(%Aw|nBV%}VcCz-7b{-osn%!5B)}4QvILc}kK@AEj4nPwM z;1#fo_$fHVEhJx|sE1Kkua?Bb$71z&J=OZK0CD{^m2gWotc_>zE`iS{272Plh`WOK znNkQ?b2jt*UsG^rtfnjOuL*b*)Iao0`L}CdqssrtK}FhXYGajuK*GpF8rDWtY$z)e zEtSbjatVCV>aA53mYbFpFteB$t{=8(y0+nCRHeT~l8WfkUr#+5?ZmqlS<6Aw2{Naz zI3A2=aF}*EuDm~-_LF_%NzpqBBi>EZ$BZphsw;1v$`_6_rZF&y_)VP1JkcO>dTk^y z=O~*p6ayHN84r%*~vSjAmwQDPKei~neqyl0znsX$2QwXf6{|{&H)LnVotZ#Rcj%RFl zY}>YN+qOEIv2ELC$4g;e#!VjWFB2U*&2 zeZ@Os!=q7}=xhcs5|}h==o)E=(04nj&=%ay+%H*lMLWROc%b=nY8JaiQbS zP#8Nz^;u;DuQbb9&oFf)Nctm-t>lCn0&V?4%L2p+fEgr04i%*B`1saIRmS{h+A~cV zlC(fk;~8DjhdQ~4fgXXfSd6AD?QVQ9v=@6Hr63JChCF6Mj?b4^Dn7|Y!6j@RFD=+^qQjf!XHX#N=VxQEGOEKAq zZKa*Pij$Y%Lv^aUSu2B(VUD>`@pErwUxQzTb;akj=p+QX3FJvSJ43ZyoO6{Nj{U_P zGI+;6*{qxrmsG#)ZwkeL;y|+Eu!$6{yC%Cuo5aB`7k2G+g-Ip)dM%N}f_uwXD5R|T1coy_X=w78hEoY`dD*$({L03KUt(FLKtI*9M>VDDaA zJ5HI)vnAY&k%R;~3xNx8g_~b(wHI?{Tdgw<>8J?Jzxu#=k_kz-i=3x)omo) z0I<~Xhm7o)5CDAEdvE`^AC?u2_*7b=W(D7Nd^5$w!fBGUCxn;uN_!r^eTs8r+(Q-J zPv7dA4i|+@$04)D)`FDtD*P7fjYF*mD_h$e3J|@yJdn|Xi4NIYe23bbE2d0-#R81& zx}vq4nKh0V5P=A@+6As;1+KV(4pJw%5sz@_=w^DN*JM9J#?>g0Aw{qIga8r3-0Eun zcWF2hrn*z*3J!+MKD}gc95sypwbs^q0 zuIDs};_rW1Fa_f-K**o9q6_9+j3^+dB$@lGxMG2 zNwVIs&`O)Ky{_obRl76e^r0^2f^PW{Es4|M`(>7Rub9J z3N5~c1p^3pJU^UV+mr_HwF5AXa8;`p@jYXT+4aORAisX*IjGCVxW62w@%pv~7D>-uWQ@S1N)9l%4Cd72<}%G0)D=>UL*K$*m{yRIHnU636I^KO201i#46A@YA)3M=<9P+|BSLvB*c0lk9DLsA)IEj4tK|ABe8Mo!!03tcMdUouza?yjsTmq+WYdx@!GeTqls=qY?`*>{zp&XD6ClyLRJ5MRR(kqh6AWYy?9`~w{67!L z-2utB4zB^)v$QG97AsF%4BM+x697VF@3}*}nehgBH+|&CAawp45=F*0WcfF+cgk2x zOd|BNp@amBnVGwv&xgF0Gu*eJm6gjcgu6IHO?Vs#j8~f;=nPmIHxgSLteXX(5eIZA zVY`H0oHhXOGdG(a;wV#-{pFg3QF~jA<_t%s>m3W7Oku5S3C=1mLUx84c_kYius3Pv zX2Tp9OyOAP$Z}`%Vu10wK8c8x-?6hpTMu$ z@7heZHA_9s!p~$RA)P9T<1`W< zo${hW5aCzc;-ct9h!s&+NC+~bMHUw?K0 z4D$}gk9kax>(S4mdfJrEOLDOWnyTR)w~5>Je<9i3V{$U{R2RpzxQ3h7(s}z~?ES26 z9I1MV!`91hNK<~sWUVPi@Doc92bd&*2+JRcwSJNv(S4Ku!}Falbp@4znzT&$+XF2d z?LN00pZqt8$O9V|(0?P@HKHFg7(NM&=A{4f`xg7(Dzc;v>xF)R6VNBUOF86XhSkZ> z73#zQ)+rbBn=FVNEa?lFIA=Do_UajMn&NwV?b5P_J*|mub*)bABeN--Xy8Q1a16=4I~l_G9+P`v>36fdA{49HArBLkZM# zVNwe*m5JfaXZXXfx-l0w&(hu+YMMu|eRQpJby@0T8qD=mL)Th;HrD#Xl!k2^;VeQt2wuEI=@E@|Zz^5w zTJ2hEYqYoA5Y}4H=h(tj0FqX27x9MFwgl1_2&Bf0>SE~Q6t3f``*JOfp8seaj1b!x zViwojcI*;3FA1(qYUtS^(p|=gZM|z^Ag6o9#^$s?A+NEhCZ@ZVz~xjK7+oS3Z;i3{ z7Ob>ESC6m{J9uNR+c!U8)rzAV$w@F zM>W!USoBbI79EN#{Fy4HEd8*?q6~p$(6&@u#AhbJcxtZMA^x3qnG2PDuO$tEXBmVp zVmUxeTT80c+AYvx!2X3VESsttN@*B_3cIIOQ^0UcjUax$ZuE@aV$G_*Su_dt8~0Mc6oNgSJ{l zO6_}CurKv9*SC4cP!rDW&+(`Y5tPVpVTel#pm|DoKhDjyL@rVOfheKd^FFu1od#S^ zImoY)Q|pg^o2PGnTRX;64w7ifPw~9Ta_wDs9)>g?(bSoAzH2*_4z)4!B+HKdXE@@I zgz4t~gV0T2S!z$>xL`^4ixZ^BfiY!W%81pt$y*oF^T})7O^T-6t_{J;c+VS6y(1d1 z?4Xr&1Z}|GyTc$VPDzE+E6E1ysV*+N1g;H z2ZFL+xTP(e0Nf<^D^fKiZ-xdx?={NhdH_l=6whl6J48>Lzj=nF)|O~ zq1Hs+nv7SZM5C2&foIeoh(Y-$UNF@MNS4e7=GVuPllwwbe>0KBD(C_(N+mU6`6@xw znZv3qk*4aXv&a^fp8dpEvP2XWaslV$$)(_ZOO1vcd66`cJHCaLm|g~B@JSX)Mb9Bv zS-3%hf4rm@pIY;Z?~TL4B`G;;CDB24-pSZ=z$mDi&2q z&sfAx84x5vo;O!@XFiK5n|(jV!@h2>TnD9OUyNT@d31=yACs~vg;p~Q$X(Y^9 zCzNE?THOhl*C3jHefG-$`LQ9~lc-W!btpWmlqpSog((*JjV?W>EC5ilDekGLMP-vH zZ&b0*Q^8Zj_I^U65FUGCGK?fR61QtDh~$w^GJ#K7z5Z(W@;TtB6rfWCH?b)R+-c)4oO_bqz)?*N|^GP%}@rNHZm*}C7ibswMDBg8hOQFxC*(-fw)<0flaQBSHd9?VaZgAO2n?ZGUtj^7q5n{YYuD8X<#MeMQ|3vL{dpK);w zedkrtqJM=3H@okI^^XF~TtKF3>||x@?29_V5Ny;-gWiPQHCt;qWa}w+GLJ3fE`felJPtg7(xj7k0@Vy^F#aheYN(3N4C&POdR1 zKLqAeW3bidGX_F(fqKu3LIxDq6cA#TP36YOuRZCzugos2PG8ceDv22c*bs(3I zJN~ozK1)KebC}n@tWZ1NWA<|ylG%?wBpa*{FFV# znXCBOtMU#$)x|6Dhw{#+J2pS4SJuTh?Xy{h+mN$aXAs=tRp%L!)3vvCM@{)NL+>{P z!|`_#EW;dN{&+xQG*?))CbrV}p&R;mJdbfIY>)G@s!}EQ1>nE0n z^lmRI1jB)V+UON-&4FW?ntje0llt%c`_+yVirc52r=A2;S+3s%+@n;JbUq02K>2}bo00g z;_zE+@V4~QHt5c-@~utA2y^WB^ze>YpK#!@o*3DjgCGL7M@Ikw^Mm9Z4504M2UAsK z{j+CQhZ$0@@m@AZo$;G4W*z%A!N)yCo%2gD8-@O23Od(lkxYV8myKKql0x2j?hnVbnOmCpz!!`xbhkNgj%iD` zAn|B$w<7822c<@Fat?aqmS06TU(~bIE_%9umElUHpO+nA*1{;|O4H^*SKCz`K_g(+ zHd+(xuxqyv3zb71vn6w!6*h>YWCH>kCW_j=*mG1)!^W(kTe2C?FMn~*YFSdgY_i7t zoz?7Y}oT$Cx*$DjBoJ;1|X_=7w9m?P2gr zY+Jxk>;>Gnp`?v;@hh2)`wXV-iqoYp)2y*itD4SD^YJv z>>~+po8^(qq1A0Lwc;>0YK>&A+A7&Hh~L-7L|KrV(i|asgkMgC_}_XPC{(TItwTWd zObiTsUS#|t(MZB%x3moCHqnjsvG+Pt8Kl~XN2p$CS*i7@Rng|`=+mgiEqkTCrnN-| z*RXNn>$VZ#h>gqrJfbD#I3c(U5Th z%7jEv;1^>n2R(yW^drTpp3`C=d1h#NOHDq6GX)RSaz{h^AB|ggk&^3|njIKr1&gni zNO>z|N_fx`#e)Rj8GGz65W7%sq_&;Rm?KDG{&0nwzINJKBRInQ)hfi=OuUCpa=e3h ze4I#N5W*p@3K(Gs^i_DMnQ9|y?J_3D^L{pZA0g7MR##^##g^{@81I_;t+F$QWOkkDj1d|_6{wzESB0q5Tvzc|wO_&y&- zOlH-9b&3ltjm`E+5wd$kq=R&ZO)tF=4f^>m`$mCI0J_m>95rax!{j2|wkKCL91+8k zaFX}w=y!nmNfpwN?E6^Lb9f7lQM=W`Qetztic9gh)YP`oq{_qcB*pSuW)VC*7pTvP z*V@$|d3CCJ<{_!rM`uCa%pG*G^@@84*~0kqKIgAFX+*qpCp`Q({8LrkKH8cz{H}KS zgZvI&sX`AE^tx1Tf@vOg z-W5ICwfkW0DkWF49j4mw=ELF{o!ONv_o*2!3_RFT?%_UVLbs`K6mW7+jNPnR&i3cXF;;N{D`WPUO43#-q?FcfHZ{xv2mV23ql{dbvArYWZ%{vy7k@=O7eYHop~ z^?<6oNWhe~&UTFb-VY+=XGAa==SGyjDCKR=%Db8|KULUAFDKFNRCW}ZQ=^d_KwZ!y z<+Xhsy*N8g#T)p(dk8;-q=uy6Dfi6Vz$P~n`hi{oMp;KWd!0=%#)#$2@y#_>8Lhn$ ze02ri(Wm?fq6y~8_2>Q|9y?;I-7pb;Oo#b)cT6~!VegFQ2*vfofh*2Q7E&=?xH3P! zwl&s&ArN;qzgcjui3rA4CZNxen-^!oc6jG>=w z7xe>4_pAQJ_a7LjH<83E#z@15Yz zXGPyp8_R2xwcNjf5dS%rUp|}aKl>cZfdAXEjQBrt0X2Qq|M9)*7El+$sP&2sM8MFe zl#vvG2P;vtXkgdwUUk?nD=^8kSWi|6N*T_Fvyif$WV61L_$yxm;{dxMBNjfMuAZ0N zmYkm||JnHbFLI{2##P5D?#7Wtm)xqX&DLAUzCQC+Q8M$cbk#+RUTcl+gnW=M^)yC+ zOg+)C&ah2}9Z=WEc~V}ZS1C&$*KC3fKunkwVu~dcOK#&13nyzo@|+4?Os%CL6^dJw zV>^RG6zkUV7kL}bLhK-w6(uYqb{QyglwIs|QICN&H!|le6CWH6Fddtq?^bcwAG-}} z`~w&NT4q|`O9Q9rfqutyN1!i*0)RoITo4 z1&i?m+fvJZmF#VpCSGr$L$V`y!u5Fw=B=<=ig=5ww$Lc5#(E)05b2uDLdO}t`D-KI z2!Z#eO2<@#^PmWj>+=kZeR1GxK}&IsQy!_`&8Z!xM`coIJ8>mRC^@k8qZ{KSVnNJ4 zLgpt1@G!zp54F||FnLnH6y(pK!f^MYGoI*_MaNanM2@ir*rma?`aJ>AWHMDXyyx#L_t4Vg_q2VeqNJq zF5t02E1aMR6&8<>vgP%)d}XIo4mIu6HC#Gaua8F{@h$iQm&NjtHndep0*zj>ZExp4 zBWQ{Cobdj$=%W4G^Bn&mI&w8^RW~Hi`J7}@zllb6eF1NuPVmu&l!uaxJjKh? zS@+tLthBb8Pg7cgQO5y-s4ydh>_RoGoG)pE#GzK+o=Qmglwn7btse7@r|M4$~1x4O+w%tyGvXEhWXCU0tdKo?i%?G$b{eTi*1bk$4p^>9Joi~ zIifT}cb%f|?7p!JxOSp(CuCm8bVoMD)?|)~{06QBaXb(XK>KgS@yhAW!4)2@eY1W&Vu%&@EVZz_1n8kut7m0SoKLn~9T8yFsXL~Yyq?!xMMKBIO3H<0WW_SE@ z`M6&ftdAiMfEx}_)~W2d6L5i3TVgDe&#qUO^){G^yff`caCZl3O9)y$Kxj8xojSD2 z*|VRjO+hJUVU7Xzp8hmF80+<`*h;1NP(zoLoN8^V-T6!WAoHn&loLDcz`lPq-2ZFp zcLkST=^sN|&*U!E3`o-o5qOc1G6g1BpcK)t%k@Em$TFQis4YG{7+z;EiRp6z!ryh8 zYcR>3%i~_zv(AL27tDww`Nb;w^#^~13vW`MeJvAHwl#T|yA?>fK6cC?$q3T{>ZIe< zuEM5y__^2MgQV$QgYOjR48~q;E9VudU_=~Y%Nd6by*;gbp4+2~&4}bp)s)9`W?aRn# zD#81=n@EZOEykZFW=%YG^mmUWV=oki%>#SP_0v+oPkgh77^QD);@2sVkb6A^a*Ir+Hf6T50_PpJ5 zevz#}!Hm^VWwl?t+FaguVW?i3hL~>HIPBePv-33s-CIn}yL}jf%BezNT3x=4-N-yA z1{B!Z&o+6FFt69y^5m8I)|Q!DRllpb@O`@VtLv%*^BI4`wg(%JM<7>7VQV$wPthU| zMw*F-#Rx;90=<+ef1wRjtg!Rv119sB&V5(A7U$a5r?dWG>N==CF88ti9LrmBF^fSi zqBQpJYihlmz*c2qShWbbNx6=SGA`tZimq9{W4#g@A=krfqqVbrX-kTd*28GC4TYyv zdnGZGL-`Gn+t}7eq9>E`g2VNLy50n81>;ji!{k#`Lb(qGI=<&2PUAZ0()S&9xd~q6 z!vtQ_pbAg~$Py9K|V1huN@8;f9U`>MAgmuzGb zksHQkM1k#B)+-$LTj6-CCe*jkOqM4fdh(=!Kanz>b|WuxU4F<^YO-L4e}5 z2*o@6VO#>B@TKN;Wtn-wmlMhP)YG6RmwOPk&?#oq;0@VvGd9#!MJ`HkaOPXNbEPasU-85zHthq-@vhLLYEx%PuwCFPBAxTVyh`^|H#7L z*668Vn!ZMUNs~o6MS;K@Dl$x_QHK>KDd!5dFsf-M%APpxt|0HM5%6he-wexj_;FS6 z)0AP%Qll$7`o~@Oz)`ZKXN@GkAY`K&!h6_o&m3=*sQqrh)db{~5)bW{p(C?�bc@~7Tbxq}cZf+XYzltPRiCI7L=04Q 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ÿ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

    X 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 literal 0 HcmV?d00001 diff --git a/compatibility/libs/ClueScrolls-api.jar b/compatibility/libs/ClueScrolls-api.jar deleted file mode 100644 index 38234c62f02d21e6e0c86b843b4042a4dddfe7f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1276918 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

  • {r*6&Fn8()w(Z?r8ykQ)_kh;d55{>Kh^D1SH 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 literal 0 HcmV?d00001 diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index b330eba87..cf99180a0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -53,7 +53,6 @@ import net.momirealms.customcrops.compatibility.VaultHook; import net.momirealms.customcrops.manager.AdventureManagerImpl; import net.momirealms.customcrops.manager.HologramManager; -import net.momirealms.customcrops.manager.PacketManager; import net.momirealms.customcrops.mechanic.item.impl.VariationCrop; import net.momirealms.customcrops.mechanic.misc.TempFakeItem; import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; @@ -61,7 +60,7 @@ import net.momirealms.customcrops.util.ConfigUtils; import net.momirealms.customcrops.util.ItemUtils; import net.momirealms.sparrow.heart.SparrowHeart; -import net.momirealms.sparrow.heart.argument.HandSlot; +import net.momirealms.sparrow.heart.feature.inventory.HandSlot; import org.bukkit.*; import org.bukkit.block.BlockFace; import org.bukkit.configuration.ConfigurationSection; From f79948ecae93ddafc3d9ab517beca5016232c1d7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 8 Jul 2024 01:54:57 +0800 Subject: [PATCH 051/329] 3.5.6 --- .../world/SynchronizedCompoundMap.java | 26 +++++++++++++++++++ build.gradle.kts | 2 +- plugin/build.gradle.kts | 2 +- .../customcrops/manager/CommandManager.java | 15 ++++++++++- .../condition/ConditionManagerImpl.java | 12 +++++++++ .../mechanic/item/ItemManagerImpl.java | 2 +- .../custom/itemsadder/ItemsAdderProvider.java | 6 ++++- 7 files changed, 60 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java index c6f24eb2b..0a9c8b633 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java @@ -20,7 +20,9 @@ import com.flowpowered.nbt.CompoundMap; import com.flowpowered.nbt.Tag; +import java.util.Map; import java.util.Objects; +import java.util.StringJoiner; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -64,4 +66,28 @@ public boolean equals(Object o) { SynchronizedCompoundMap that = (SynchronizedCompoundMap) o; return Objects.equals(compoundMap, that.compoundMap); } + + @Override + public String toString() { + return compoundMapToString(compoundMap); + } + + private String compoundMapToString(CompoundMap compoundMap) { + StringJoiner joiner = new StringJoiner(", "); + for (Map.Entry> entry : compoundMap.entrySet()) { + Tag tag = entry.getValue(); + String tagValue; + switch (tag.getType()) { + case TAG_STRING, TAG_BYTE, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_INT_ARRAY, TAG_LONG, TAG_SHORT, TAG_SHORT_ARRAY, TAG_LONG_ARRAY, TAG_BYTE_ARRAY -> + tagValue = tag.getValue().toString(); + case TAG_COMPOUND -> tagValue = compoundMapToString(tag.getAsCompoundTag().get().getValue()); + case TAG_LIST -> tagValue = tag.getAsListTag().get().getValue().toString(); + default -> { + continue; + } + } + joiner.add("\"" + entry.getKey() + "\":\"" + tagValue + "\""); + } + return "{" + joiner + "}"; + } } diff --git a/build.gradle.kts b/build.gradle.kts index 54d408291..2237f13b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.5" + project.version = "3.5.6" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index fd71b84b2..82cca66e9 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -26,7 +26,7 @@ dependencies { compileOnly("net.Indyuce:MMOCore-API:1.12-SNAPSHOT") compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.23") compileOnly("com.github.Zrips:Jobs:4.17.2") - compileOnly("dev.aurelium:auraskills-api-bukkit:2.0.0-SNAPSHOT") + compileOnly("dev.aurelium:auraskills-api-bukkit:2.1.2") // Items compileOnly("com.github.LoneDev6:api-itemsadder:3.6.2-beta-r3-b") diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java index e30cd5b64..ac96dea3d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java @@ -30,6 +30,7 @@ import net.momirealms.customcrops.api.mechanic.item.ItemType; import net.momirealms.customcrops.api.mechanic.world.ChunkPos; import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsSection; import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; @@ -37,6 +38,7 @@ import net.momirealms.customcrops.compatibility.season.InBuiltSeason; import org.bukkit.Bukkit; import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.generator.WorldInfo; import java.util.Locale; @@ -88,7 +90,7 @@ private CommandAPICommand getUnsafeCommand() { return new CommandAPICommand("unsafe") .withSubcommands( new CommandAPICommand("delete-chunk-data").executesPlayer((player, args) -> { - CustomCropsPlugin.get().getWorldManager().getCustomCropsWorld(player.getWorld()).ifPresent(customCropsWorld -> { + plugin.getWorldManager().getCustomCropsWorld(player.getWorld()).ifPresent(customCropsWorld -> { var optionalChunk = customCropsWorld.getLoadedChunkAt(ChunkPos.getByBukkitChunk(player.getChunk())); if (optionalChunk.isEmpty()) { AdventureManager.getInstance().sendMessageWithPrefix(player, "This chunk doesn't have any data."); @@ -97,6 +99,17 @@ private CommandAPICommand getUnsafeCommand() { customCropsWorld.deleteChunk(ChunkPos.getByBukkitChunk(player.getChunk())); AdventureManager.getInstance().sendMessageWithPrefix(player, "Done."); }); + }), + new CommandAPICommand("check-data").executesPlayer((player, args) -> { + Block block = player.getTargetBlockExact(10); + if (block != null) { + Optional customCropsBlock = plugin.getWorldManager().getBlockAt(SimpleLocation.of(block.getLocation())); + if (customCropsBlock.isPresent()) { + AdventureManager.getInstance().sendMessageWithPrefix(player, customCropsBlock.get().getType() + ":" + customCropsBlock.get().getCompoundMap()); + return; + } + } + AdventureManager.getInstance().sendMessageWithPrefix(player, "Data not found"); }) ); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java index 851ce4aa2..d57bd30a2 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java @@ -97,6 +97,7 @@ private void registerInbuiltConditions() { this.registerPotCondition(); this.registerLightCondition(); this.registerPointCondition(); + this.registerWorldRequirement(); } @Override @@ -199,6 +200,17 @@ private void registerCrowAttackCondition() { })); } + private void registerWorldRequirement() { + registerCondition("world", (args) -> { + HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); + return (block, offline) -> worlds.contains(block.getLocation().getWorldName()); + }); + registerCondition("!world", (args) -> { + HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); + return (block, offline) -> !worlds.contains(block.getLocation().getWorldName()); + }); + } + private void registerBiomeRequirement() { registerCondition("biome", (args) -> { HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index c9735a149..5ff8b4f61 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -133,7 +133,7 @@ public ItemManagerImpl(CustomCropsPlugin plugin, AntiGriefLib antiGriefLib) { oraxenListenerConstructor.setAccessible(true); this.listener = (AbstractCustomListener) oraxenListenerConstructor.newInstance(this); Class oraxenProviderClass = Class.forName("net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenProvider"); - Constructor oraxenProviderConstructor = oraxenProviderClass.getDeclaredConstructor(ItemManager.class); + Constructor oraxenProviderConstructor = oraxenProviderClass.getDeclaredConstructor(); oraxenProviderConstructor.setAccessible(true); this.customProvider = (CustomProvider) oraxenProviderConstructor.newInstance(); } catch (ReflectiveOperationException e) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java index 91773389a..53a1cb848 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java @@ -106,6 +106,10 @@ public String getEntityID(Entity entity) { @Override public boolean isFurniture(Entity entity) { - return CustomFurniture.byAlreadySpawned(entity) != null; + try { + return CustomFurniture.byAlreadySpawned(entity) != null; + } catch (Exception e) { + return false; + } } } From 64e49dbdeb919ac35c66f9604f6b26d543ca6efb Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 9 Jul 2024 23:27:17 +0800 Subject: [PATCH 052/329] 3.5.7 --- build.gradle.kts | 2 +- plugin/build.gradle.kts | 2 +- .../momirealms/customcrops/mechanic/item/ItemManagerImpl.java | 3 +++ .../main/java/net/momirealms/customcrops/util/ItemUtils.java | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2237f13b2..0b1428332 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.6" + project.version = "3.5.7" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 82cca66e9..dcfe4edbf 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -57,7 +57,7 @@ dependencies { implementation("net.kyori:adventure-platform-bukkit:4.3.3") implementation("net.kyori:adventure-text-minimessage:4.17.0") implementation("net.kyori:adventure-text-serializer-legacy:4.17.0") - implementation("com.github.Xiao-MoMi:AntiGriefLib:0.11") + implementation("com.github.Xiao-MoMi:AntiGriefLib:0.12") // implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.16") implementation("com.flowpowered:flow-nbt:2.0.2") implementation("com.saicone.rtag:rtag:1.5.4") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java index 5ff8b4f61..0472a9360 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -2660,6 +2660,9 @@ public void updatePotState(Location location, Pot pot, boolean hasWater, Fertili Pot currentPot = getPotByBlock(block); if (currentPot != pot) { plugin.getWorldManager().removePotAt(SimpleLocation.of(location)); + if (currentPot != null) { + plugin.getWorldManager().addPotAt(new MemoryPot(SimpleLocation.of(location), currentPot.getKey()), SimpleLocation.of(location)); + } return; } if (pot.isVanillaBlock()) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java index 89cfed459..eb8e5042a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java @@ -85,7 +85,7 @@ public static void giveItem(Player player, ItemStack itemStack, int amount) { public static void increaseDurability(ItemStack itemStack, int amount) { if (itemStack == null || itemStack.getType() == Material.AIR) return; - Item item = BukkitItemFactory.getInstance().wrap(itemStack); + Item item = BukkitItemFactory.getInstance().wrap(itemStack.clone()); int damage = Math.max(item.damage().orElse(0) - amount, 0); item.damage(damage); itemStack.setItemMeta(item.load().getItemMeta()); @@ -104,7 +104,7 @@ public static void decreaseDurability(Player player, ItemStack itemStack, int am if (Math.random() > (double) 1 / (unBreakingLevel + 1)) { return; } - Item item = BukkitItemFactory.getInstance().wrap(itemStack); + Item item = BukkitItemFactory.getInstance().wrap(itemStack.clone()); int damage = item.damage().orElse(0) + amount; if (damage > item.maxDamage().orElse((int) itemStack.getType().getMaxDurability())) { itemStack.setAmount(0); From 366b221ba3e417ff319f02c591ce7fae11b85a45 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Wed, 10 Jul 2024 16:42:30 +0800 Subject: [PATCH 053/329] return if itemMeta is null --- .../customcrops/mechanic/action/ActionManagerImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index cf99180a0..1baa32029 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -827,7 +827,8 @@ private void registerItemDurabilityAction() { return state -> { if (Math.random() > chance) return; ItemStack itemStack = state.getItemInHand(); - + if (itemStack.getItemMeta() == null) + return; if (amount > 0) { ItemUtils.increaseDurability(itemStack, amount); } else { From 6b81f935bcea9031bf24cdc6d9e35f15a7f196c4 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Sun, 14 Jul 2024 17:16:07 +0800 Subject: [PATCH 054/329] 3.5.7.2 --- README.md | 1 + build.gradle.kts | 2 +- .../main/java/net/momirealms/customcrops/util/ConfigUtils.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 525eaab9d..04f589950 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Custom-Crops ![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/Xiao-MoMi/Custom-Crops) +![Code Size](https://img.shields.io/github/languages/code-size/Xiao-MoMi/Custom-Crops) ![bStats Servers](https://img.shields.io/bstats/servers/16593) ![bStats Players](https://img.shields.io/bstats/players/16593) ![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Crops) diff --git a/build.gradle.kts b/build.gradle.kts index 0b1428332..b78c03e47 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.7" + project.version = "3.5.7.2" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java index dc08207df..f25b5ce22 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java @@ -279,7 +279,7 @@ public static HashMap> getFertilizedPotMap( FertilizerType type = switch (entry.getKey()) { case "quality" -> FertilizerType.QUALITY; case "yield-increase" -> FertilizerType.YIELD_INCREASE; - case "variation-increase" -> FertilizerType.VARIATION; + case "variation" -> FertilizerType.VARIATION; case "soil-retain" -> FertilizerType.SOIL_RETAIN; case "speed-grow" -> FertilizerType.SPEED_GROW; default -> null; From 4b359a3cf950e4b6d0c160605e5e39c6561b9eed Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Fri, 19 Jul 2024 00:58:38 +0800 Subject: [PATCH 055/329] Fix particle on 1.20.5+ --- build.gradle.kts | 2 +- .../mechanic/action/ActionManagerImpl.java | 3 +- .../customcrops/util/ParticleUtils.java | 35 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 plugin/src/main/java/net/momirealms/customcrops/util/ParticleUtils.java diff --git a/build.gradle.kts b/build.gradle.kts index b78c03e47..ee76be12c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.7.2" + project.version = "3.5.7.3" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java index 1baa32029..da619f2be 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -59,6 +59,7 @@ import net.momirealms.customcrops.util.ClassUtils; import net.momirealms.customcrops.util.ConfigUtils; import net.momirealms.customcrops.util.ItemUtils; +import net.momirealms.customcrops.util.ParticleUtils; import net.momirealms.sparrow.heart.SparrowHeart; import net.momirealms.sparrow.heart.feature.inventory.HandSlot; import org.bukkit.*; @@ -743,7 +744,7 @@ private void registerBreakAction() { private void registerParticleAction() { registerAction("particle", (args, chance) -> { if (args instanceof ConfigurationSection section) { - Particle particleType = Particle.valueOf(section.getString("particle", "ASH").toUpperCase(Locale.ENGLISH)); + Particle particleType = ParticleUtils.getParticle(section.getString("particle", "ASH").toUpperCase(Locale.ENGLISH)); double x = section.getDouble("x",0); double y = section.getDouble("y",0); double z = section.getDouble("z",0); diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ParticleUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ParticleUtils.java new file mode 100644 index 000000000..5162f5b73 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/util/ParticleUtils.java @@ -0,0 +1,35 @@ +/* + * 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.util; + +import org.bukkit.Particle; + +public class ParticleUtils { + + public static Particle getParticle(String particle) { + try { + return Particle.valueOf(particle); + } catch (IllegalArgumentException e) { + return switch (particle) { + case "REDSTONE" -> Particle.valueOf("DUST"); + case "VILLAGER_HAPPY" -> Particle.valueOf("HAPPY_VILLAGER"); + default -> Particle.valueOf(particle); + }; + } + } +} From ca694976a4de9259f046791790f50232fba6fc70 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 22 Jul 2024 23:41:23 +0800 Subject: [PATCH 056/329] Update plugin.yml --- plugin/src/main/resources/plugin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 6d3de5db1..a621d75ac 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -28,6 +28,7 @@ softdepend: - GriefPrevention - BentoBox - IridiumSkyblock + - MythicMobs - KingdomsX - Landlord - Lands From fed8a2229bcf97afc2e2693d6c93314282013eec Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Mon, 22 Jul 2024 23:41:45 +0800 Subject: [PATCH 057/329] Update build.gradle.kts --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index ee76be12c..aad295247 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.7.3" + project.version = "3.5.7.4" apply() apply(plugin = "java") From c74678b957ac13cb201c730b73b98ed61d45f77f Mon Sep 17 00:00:00 2001 From: inrhor Date: Wed, 31 Jul 2024 19:55:13 +0800 Subject: [PATCH 058/329] =?UTF-8?q?-=20ItemStack=E6=B7=BB=E5=8A=A0.clone()?= =?UTF-8?q?=E4=BF=AE=E5=A4=8DItemsAdder=20v4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mechanic/item/custom/itemsadder/ItemsAdderProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java index 53a1cb848..bc184d342 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java @@ -92,7 +92,7 @@ public ItemStack getItemStack(String id) { if (customStack == null) { return null; } - return customStack.getItemStack(); + return customStack.getItemStack().clone(); } @Override From 8aac42548020927ffd5e1fea31f74af5005746a7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 13 Aug 2024 15:11:01 +0800 Subject: [PATCH 059/329] 3.5.9 --- .../customcrops/api/CustomCropsPlugin.java | 2 + .../item/custom/AbstractCustomListener.java | 12 ++--- build.gradle.kts | 2 +- plugin/build.gradle.kts | 22 ++++---- plugin/libs/AdvancedSeasons-API.jar | Bin 64703 -> 1542755 bytes .../customcrops/CustomCropsPluginImpl.java | 21 +++++++- .../compatibility/IntegrationManagerImpl.java | 2 +- .../compatibility/quest/BattlePassHook.java | 7 ++- .../compatibility/quest/BetonQuestHook.java | 49 +++++++++++------- .../season/AdvancedSeasonsImpl.java | 28 +++++----- .../libraries/dependencies/Dependency.java | 6 +-- .../dependencies/DependencyManagerImpl.java | 3 -- .../mechanic/item/ItemManagerImpl.java | 3 +- .../mechanic/world/WorldManagerImpl.java | 11 ++-- .../world/adaptor/BukkitWorldAdaptor.java | 2 +- .../world/adaptor/SlimeWorldAdaptor.java | 49 ++++++++++++++---- 16 files changed, 139 insertions(+), 80 deletions(-) diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java index 78cf379c6..a24ef6e57 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java +++ b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java @@ -121,6 +121,8 @@ public PlaceholderManager getPlaceholderManager() { public abstract void reload(); + public abstract boolean isHookedPluginEnabled(String hooked, String... versionPrefix); + public abstract boolean doesHookedPluginExist(String plugin); public abstract String getServerVersion(); diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java index ce921bc14..2d173e67e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/custom/AbstractCustomListener.java @@ -31,6 +31,7 @@ import net.momirealms.customcrops.api.mechanic.world.level.WorldGlass; import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; import net.momirealms.customcrops.api.util.EventUtils; +import net.momirealms.customcrops.api.util.LogUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -166,12 +167,7 @@ public void onPlaceBlock(BlockPlaceEvent event) { final Location location = block.getLocation(); Optional customCropsBlock = CustomCropsPlugin.get().getWorldManager().getBlockAt(SimpleLocation.of(location)); if (customCropsBlock.isPresent()) { - if (customCropsBlock.get() instanceof WorldPot || customCropsBlock.get() instanceof WorldGlass) { - CustomCropsPlugin.get().getWorldManager().removeAnythingAt(SimpleLocation.of(location)); - } else { - event.setCancelled(true); - return; - } + CustomCropsPlugin.get().getWorldManager().removeAnythingAt(SimpleLocation.of(location)); } this.onPlaceBlock( event.getPlayer(), @@ -273,12 +269,12 @@ public void onItemDamage(PlayerItemDamageEvent event) { } } - @EventHandler (ignoreCancelled = true) + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onExplosion(EntityExplodeEvent event) { this.itemManager.handleExplosion(event.getEntity(), event.blockList(), event); } - @EventHandler (ignoreCancelled = true) + @EventHandler (ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onExplosion(BlockExplodeEvent event) { this.itemManager.handleExplosion(null, event.blockList(), event); } diff --git a/build.gradle.kts b/build.gradle.kts index aad295247..683b23a44 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.5.7.4" + project.version = "3.5.9" apply() apply(plugin = "java") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index ef52618c7..4ee562599 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,18 +1,18 @@ dependencies { // Platform - compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT") + compileOnly("dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT") compileOnly("com.infernalsuite.aswm:api:1.20.4-R0.1-SNAPSHOT") // Command - compileOnly("dev.jorel:commandapi-bukkit-core:9.4.1") + compileOnly("dev.jorel:commandapi-bukkit-core:9.5.3") // Common hooks - compileOnly("me.clip:placeholderapi:2.11.5") + compileOnly("me.clip:placeholderapi:2.11.6") compileOnly("com.comphenix.protocol:ProtocolLib:5.1.0") compileOnly("com.github.MilkBowl:VaultAPI:1.7") // Utils - compileOnly("dev.dejvokep:boosted-yaml:1.3.4") + compileOnly("dev.dejvokep:boosted-yaml:1.3.6") compileOnly("commons-io:commons-io:2.15.1") compileOnly("com.google.code.gson:gson:2.10.1") compileOnly("net.objecthunter:exp4j:0.4.8") @@ -34,11 +34,11 @@ dependencies { compileOnly("pers.neige.neigeitems:NeigeItems:1.16.24") compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT") compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT") - compileOnly("io.lumine:Mythic-Dist:5.3.5") + compileOnly("io.lumine:Mythic-Dist:5.6.1") compileOnly("io.lumine:MythicCrucible-Dist:2.1.0-SNAPSHOT") // Quests - compileOnly("org.betonquest:betonquest:2.0.0") + compileOnly("org.betonquest:betonquest:2.1.3") compileOnly(files("libs/BattlePass-4.0.6-api.jar")) compileOnly(files("libs/ClueScrolls-api.jar")) @@ -53,17 +53,17 @@ dependencies { implementation(project(":oraxen-j21")) implementation(project(":legacy-api")) - implementation(files("libs/Sparrow-Heart-0.19.jar")) + implementation("net.kyori:adventure-api:4.17.0") implementation("net.kyori:adventure-platform-bukkit:4.3.3") implementation("net.kyori:adventure-text-minimessage:4.17.0") implementation("net.kyori:adventure-text-serializer-legacy:4.17.0") implementation("com.github.Xiao-MoMi:AntiGriefLib:0.12") -// implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.16") + implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.35") implementation("com.flowpowered:flow-nbt:2.0.2") - implementation("com.saicone.rtag:rtag:1.5.4") - implementation("com.saicone.rtag:rtag-item:1.5.4") - implementation("com.github.luben:zstd-jni:1.5.6-2") + implementation("com.saicone.rtag:rtag:1.5.5") + implementation("com.saicone.rtag:rtag-item:1.5.5") + implementation("com.github.luben:zstd-jni:1.5.6-4") } tasks { diff --git a/plugin/libs/AdvancedSeasons-API.jar b/plugin/libs/AdvancedSeasons-API.jar index 19ddf212c31fbb61fe4c2dfc7a9ee7ecd578e0e0..0d84a7dd9092154aab7d0c9295d5035a5eb1d8aa 100644 GIT binary patch literal 1542755 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%0AAeGWAU3aMyMKuPqA~$a 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

    L$3P&ox28w;G@CA@&4ND-M&YzC; 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 IWfQ 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

    8torUCD4R^%E1hLd-7bl!4w8H>T@ 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

    K}W1xT8E^QK6agZ%W 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�HiJT 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�W4t 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 qui@R#{zdPWT%D diff --git a/plugin/libs/Sparrow-Heart-0.19.jar b/plugin/libs/Sparrow-Heart-0.19.jar new file mode 100644 index 0000000000000000000000000000000000000000..669b9bb14c5f4391db2e7e6dc84ec93710b97d9a GIT binary patch literal 177287 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