From 9e5117f4017a5243013bd033bc41474e9b24f2df Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 27 Jun 2023 00:06:45 +0800 Subject: [PATCH] 3.3.0.0 --- build.gradle | 6 +- .../api/customplugin/PlatformInterface.java | 4 +- .../customcrops/api/object/ItemMode.java | 5 +- .../api/object/OfflineReplaceTask.java | 28 ++ .../api/object/basic/ConfigManager.java | 4 +- .../api/object/condition/DeathCondition.java | 47 +-- .../customcrops/api/object/pot/Pot.java | 3 +- .../customcrops/api/object/pot/PotConfig.java | 2 +- .../customcrops/api/object/world/CCChunk.java | 62 +++- .../customcrops/api/object/world/CCWorld.java | 338 +++++++++++------- .../api/object/world/WorldDataManager.java | 8 + src/main/resources/config.yml | 9 +- 12 files changed, 340 insertions(+), 176 deletions(-) create mode 100644 src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java diff --git a/build.gradle b/build.gradle index 17deb71..4bb5c78 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'net.momirealms' -version = '3.2.5.0' +version = '3.3.0.0-beta' repositories { mavenCentral() @@ -26,10 +26,10 @@ repositories { dependencies { compileOnly ('dev.dejvokep:boosted-yaml:1.3.1') compileOnly ('commons-io:commons-io:2.11.0') - compileOnly ('io.papermc.paper:paper-api:1.20-R0.1-SNAPSHOT') + compileOnly ('io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT') compileOnly ('me.clip:placeholderapi:2.11.3') compileOnly ('com.github.LoneDev6:api-itemsadder:3.4.1e') - compileOnly('com.github.oraxen:oraxen:1.157.2') + compileOnly ('com.github.oraxen:oraxen:1.157.2') compileOnly ('io.lumine:Mythic-Dist:5.0.3-SNAPSHOT') compileOnly ('com.willfp:eco:6.60.0') compileOnly ('com.willfp:EcoJobs:3.13.0') diff --git a/src/main/java/net/momirealms/customcrops/api/customplugin/PlatformInterface.java b/src/main/java/net/momirealms/customcrops/api/customplugin/PlatformInterface.java index f229659..3fc0f1f 100644 --- a/src/main/java/net/momirealms/customcrops/api/customplugin/PlatformInterface.java +++ b/src/main/java/net/momirealms/customcrops/api/customplugin/PlatformInterface.java @@ -349,7 +349,7 @@ public interface PlatformInterface { * @param itemMode itemMode */ default void placeCustomItem(Location location, String id, ItemMode itemMode) { - if (itemMode == ItemMode.TRIPWIRE) + if (itemMode == ItemMode.TRIPWIRE || itemMode == ItemMode.NOTE_BLOCK) placeTripWire(location, id); else if (itemMode == ItemMode.ITEM_FRAME) placeItemFrame(location, id); @@ -366,7 +366,7 @@ public interface PlatformInterface { * @return success or not */ default boolean removeCustomItem(Location location, ItemMode itemMode) { - if (itemMode == ItemMode.TRIPWIRE || itemMode == ItemMode.CHORUS) + if (itemMode == ItemMode.TRIPWIRE || itemMode == ItemMode.CHORUS || itemMode == ItemMode.NOTE_BLOCK) return removeCustomBlock(location); else if (itemMode == ItemMode.ITEM_FRAME) return removeItemFrame(location); diff --git a/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java b/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java index ca9e242..2f29bdd 100644 --- a/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java +++ b/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java @@ -17,11 +17,14 @@ package net.momirealms.customcrops.api.object; -public enum ItemMode { +import java.io.Serializable; + +public enum ItemMode implements Serializable { ARMOR_STAND, TRIPWIRE, ITEM_FRAME, ITEM_DISPLAY, + NOTE_BLOCK, CHORUS } diff --git a/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java b/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java new file mode 100644 index 0000000..97a2878 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java @@ -0,0 +1,28 @@ +package net.momirealms.customcrops.api.object; + +import java.io.Serializable; + +public class OfflineReplaceTask implements Serializable { + + 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/src/main/java/net/momirealms/customcrops/api/object/basic/ConfigManager.java b/src/main/java/net/momirealms/customcrops/api/object/basic/ConfigManager.java index af80308..fb8bead 100644 --- a/src/main/java/net/momirealms/customcrops/api/object/basic/ConfigManager.java +++ b/src/main/java/net/momirealms/customcrops/api/object/basic/ConfigManager.java @@ -69,6 +69,7 @@ public class ConfigManager extends Function { public static boolean onlyInLoadedChunks; public static boolean enableCorruptionFixer; public static boolean debugWorld; + public static boolean updateDuringLoading; private final HashMap cropPerWorld; private final CustomCrops plugin; @@ -99,15 +100,16 @@ public class ConfigManager extends Function { debugCorruption = config.getBoolean("debug.log-corruption-fixer", false); debugWorld = config.getBoolean("debug.log-world-state", false); loadWorlds(Objects.requireNonNull(config.getConfigurationSection("worlds"))); - loadOptimization(Objects.requireNonNull(config.getConfigurationSection("optimization"))); loadScheduleSystem(Objects.requireNonNull(config.getConfigurationSection("schedule-system"))); loadMechanic(Objects.requireNonNull(config.getConfigurationSection("mechanics"))); loadOtherSetting(Objects.requireNonNull(config.getConfigurationSection("other-settings"))); + loadOptimization(Objects.requireNonNull(config.getConfigurationSection("optimization"))); } private void loadOptimization(ConfigurationSection section) { enableLimitation = section.getBoolean("limitation.growing-crop-amount.enable", true); maxCropPerChunk = section.getInt("limitation.growing-crop-amount.default", 64); + updateDuringLoading = !ConfigManager.onlyInLoadedChunks && section.getBoolean("only-update-during-chunk-loading", false); List worldSettings = section.getStringList("limitation.growing-crop-amount.worlds"); for (String setting : worldSettings) { String[] split = setting.split(":", 2); diff --git a/src/main/java/net/momirealms/customcrops/api/object/condition/DeathCondition.java b/src/main/java/net/momirealms/customcrops/api/object/condition/DeathCondition.java index 2a7fe97..b144f40 100644 --- a/src/main/java/net/momirealms/customcrops/api/object/condition/DeathCondition.java +++ b/src/main/java/net/momirealms/customcrops/api/object/condition/DeathCondition.java @@ -19,6 +19,9 @@ package net.momirealms.customcrops.api.object.condition; import net.momirealms.customcrops.CustomCrops; import net.momirealms.customcrops.api.object.ItemMode; +import net.momirealms.customcrops.api.object.ItemType; +import net.momirealms.customcrops.api.object.OfflineReplaceTask; +import net.momirealms.customcrops.api.object.basic.ConfigManager; import net.momirealms.customcrops.api.object.world.SimpleLocation; import org.bukkit.Chunk; import org.bukkit.Location; @@ -49,32 +52,36 @@ public class DeathCondition { public void applyDeadModel(SimpleLocation simpleLocation, ItemMode itemMode) { Location location = simpleLocation.getBukkitLocation(); if (location == null) return; + + if (location.getWorld().isChunkLoaded(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4)) { + replaceDeadModels(location, itemMode); + return; + } + + if (ConfigManager.updateDuringLoading) { + CustomCrops.getInstance().getWorldDataManager().addOfflineTask(simpleLocation, new OfflineReplaceTask(dead_model, ItemType.CROP, itemMode)); + return; + } + CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); if (itemMode == ItemMode.ITEM_FRAME || itemMode == ItemMode.ITEM_DISPLAY) { CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { chunk.getEntities(); return chunk.isEntitiesLoaded(); }); - loadEntities.whenComplete((result, throwable) -> - CustomCrops.getInstance().getScheduler().callSyncMethod(() -> { - if (CustomCrops.getInstance().getPlatformInterface().removeCustomItem(location, itemMode)) { - if (dead_model != null) { - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, dead_model, itemMode); - } - } - return null; - })); - } - else { - asyncGetChunk.whenComplete((result, throwable) -> - CustomCrops.getInstance().getScheduler().callSyncMethod(() -> { - if (CustomCrops.getInstance().getPlatformInterface().removeCustomItem(location, itemMode)) { - if (dead_model != null) { - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, dead_model, itemMode); - } - } - return null; - })); + loadEntities.whenComplete((result, throwable) -> replaceDeadModels(location, itemMode)); + } else { + asyncGetChunk.whenComplete((result, throwable) -> replaceDeadModels(location, itemMode)); } } + + private void replaceDeadModels(Location location, ItemMode itemMode) { + CustomCrops.getInstance().getScheduler().runTask(() -> { + if (CustomCrops.getInstance().getPlatformInterface().removeCustomItem(location, itemMode)) { + if (dead_model != null) { + CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, dead_model, itemMode); + } + } + }); + } } diff --git a/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java b/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java index 7b8c18d..5935e4c 100644 --- a/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java +++ b/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java @@ -58,8 +58,7 @@ public class Pot implements Serializable { if (water == 0) { this.water = Math.min(getConfig().getMaxStorage(), amount); return true; - } - else { + } else { this.water = Math.min(getConfig().getMaxStorage(), water + amount); return false; } diff --git a/src/main/java/net/momirealms/customcrops/api/object/pot/PotConfig.java b/src/main/java/net/momirealms/customcrops/api/object/pot/PotConfig.java index 6878bae..85b05b6 100644 --- a/src/main/java/net/momirealms/customcrops/api/object/pot/PotConfig.java +++ b/src/main/java/net/momirealms/customcrops/api/object/pot/PotConfig.java @@ -110,7 +110,7 @@ public class PotConfig { return key; } - public boolean isEnableFertilized() { + public boolean enableFertilizedLooks() { return enableFertilized; } } diff --git a/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java b/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java index 0f8dcdc..d863cd3 100644 --- a/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java +++ b/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java @@ -18,6 +18,8 @@ package net.momirealms.customcrops.api.object.world; import net.momirealms.customcrops.CustomCrops; +import net.momirealms.customcrops.api.customplugin.PlatformInterface; +import net.momirealms.customcrops.api.object.OfflineReplaceTask; import net.momirealms.customcrops.api.object.basic.ConfigManager; import net.momirealms.customcrops.api.object.crop.GrowingCrop; import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; @@ -31,12 +33,8 @@ import org.bukkit.block.data.type.Farmland; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.Serial; -import java.io.Serializable; -import java.util.Collections; -import java.util.HashSet; -import java.util.Random; -import java.util.Set; +import java.io.*; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; @@ -48,6 +46,7 @@ public class CCChunk implements Serializable { private final ConcurrentHashMap growingCropMap; private final ConcurrentHashMap potMap; private final ConcurrentHashMap sprinklerMap; + private ConcurrentHashMap replaceTaskMap; private final Set greenhouseSet; private final Set scarecrowSet; @@ -57,6 +56,20 @@ public class CCChunk implements Serializable { this.sprinklerMap = new ConcurrentHashMap<>(16); this.greenhouseSet = Collections.synchronizedSet(new HashSet<>(64)); this.scarecrowSet = Collections.synchronizedSet(new HashSet<>(4)); + this.replaceTaskMap = new ConcurrentHashMap<>(64); + } + + @Serial + private void writeObject(ObjectOutputStream oos) throws IOException { + oos.defaultWriteObject(); + } + + @Serial + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + if (replaceTaskMap == null) { + replaceTaskMap = new ConcurrentHashMap<>(64); + } } public void removeCropData(SimpleLocation simpleLocation) { @@ -114,7 +127,7 @@ public class CCChunk implements Serializable { } public boolean isUseless() { - return growingCropMap.size() == 0 && potMap.size() == 0 && greenhouseSet.size() == 0 && sprinklerMap.size() == 0 && scarecrowSet.size() == 0; + return growingCropMap.size() == 0 && potMap.size() == 0 && greenhouseSet.size() == 0 && sprinklerMap.size() == 0 && scarecrowSet.size() == 0 && replaceTaskMap.size() == 0; } @Nullable @@ -199,4 +212,39 @@ public class CCChunk implements Serializable { CustomCrops.getInstance().getPlatformInterface().placeNoteBlock(location, replacer); } } + + public void executeReplaceTask() { + PlatformInterface platform = CustomCrops.getInstance().getPlatformInterface(); + for (Map.Entry entry : replaceTaskMap.entrySet()) { + SimpleLocation simpleLocation = entry.getKey(); + String id = entry.getValue().getId(); + if (id == null) { + platform.removeCustomItem(entry.getKey().getBukkitLocation(), entry.getValue().getItemMode()); + continue; + } + switch (entry.getValue().getItemType()) { + case POT -> { + Pot pot =getPotData(simpleLocation); + if (pot == null) { + String blockID = platform.getBlockID(simpleLocation.getBukkitLocation().getBlock()); + String potKey = CustomCrops.getInstance().getPotManager().getPotKeyByBlockID(blockID); + if (potKey == null) continue; + pot = new Pot(potKey, null, 0); + } + changePotModel(simpleLocation, pot); + } + case CROP -> { + Location location = simpleLocation.getBukkitLocation(); + if (platform.removeCustomItem(location, entry.getValue().getItemMode())) { + platform.placeCustomItem(location, id, entry.getValue().getItemMode()); + } + } + } + } + replaceTaskMap.clear(); + } + + public void addReplaceTask(SimpleLocation simpleLocation, OfflineReplaceTask offlineReplaceTask) { + replaceTaskMap.put(simpleLocation, offlineReplaceTask); + } } \ No newline at end of file diff --git a/src/main/java/net/momirealms/customcrops/api/object/world/CCWorld.java b/src/main/java/net/momirealms/customcrops/api/object/world/CCWorld.java index c36de42..69a91b6 100644 --- a/src/main/java/net/momirealms/customcrops/api/object/world/CCWorld.java +++ b/src/main/java/net/momirealms/customcrops/api/object/world/CCWorld.java @@ -20,6 +20,8 @@ package net.momirealms.customcrops.api.object.world; import net.momirealms.customcrops.CustomCrops; import net.momirealms.customcrops.api.object.Function; import net.momirealms.customcrops.api.object.ItemMode; +import net.momirealms.customcrops.api.object.ItemType; +import net.momirealms.customcrops.api.object.OfflineReplaceTask; import net.momirealms.customcrops.api.object.action.Action; import net.momirealms.customcrops.api.object.action.VariationImpl; import net.momirealms.customcrops.api.object.basic.ConfigManager; @@ -55,15 +57,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; import java.util.*; import java.util.concurrent.*; public class CCWorld extends Function { private final String worldName; - private final Reference world; + private final World world; private final ConcurrentHashMap chunkMap; private final ScheduledThreadPoolExecutor schedule; private long currentDay; @@ -86,7 +86,7 @@ public class CCWorld extends Function { this.chunksFolder = ConfigUtils.getFile(world, "chunks"); this.dateFile = ConfigUtils.getFile(world, "data.yml"); this.corruptedFile = ConfigUtils.getFile(world, "corrupted.yml"); - this.world = new WeakReference<>(world); + this.world = world; this.chunkMap = new ConcurrentHashMap<>(64); this.schedule = new ScheduledThreadPoolExecutor(ConfigManager.corePoolSize); this.schedule.setMaximumPoolSize(ConfigManager.maxPoolSize); @@ -235,13 +235,12 @@ public class CCWorld extends Function { private void scheduleTask() { if (this.timerTask == null) { this.timerTask = plugin.getScheduler().runTaskTimerAsync(() -> { - World current = world.get(); - if (current != null) { + if (world != null) { if (ConfigManager.debugScheduler) { Log.info("Queue size: " + schedule.getQueue().size() + " Completed: " + schedule.getCompletedTaskCount()); } - long day = current.getFullTime() / 24000; - long time = current.getTime(); + long day = world.getFullTime() / 24000; + long time = world.getTime(); this.tryDayCycleTask(time, day); this.timerTask(); } else { @@ -334,27 +333,35 @@ public class CCWorld extends Function { @SuppressWarnings("ResultOfMethodCallIgnored") public void loadChunk(ChunkCoordinate chunkCoordinate) { - if (!ConfigManager.onlyInLoadedChunks) return; - File file = new File(chunksFolder, chunkCoordinate.getFileName() + ".ccdata"); - if (file.exists()) { - boolean delete = false; - try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) { - CCChunk chunk = (CCChunk) ois.readObject(); - if (chunk.isUseless()) { - delete = true; - } else { - chunkMap.put(chunkCoordinate, chunk); - if (!loadInPoint.contains(chunkCoordinate)) { - chunk.scheduleGrowTask(this, -1); + if (ConfigManager.onlyInLoadedChunks) { + File file = new File(chunksFolder, chunkCoordinate.getFileName() + ".ccdata"); + if (file.exists()) { + boolean delete = false; + try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) { + CCChunk chunk = (CCChunk) ois.readObject(); + if (chunk.isUseless()) { + delete = true; + } else { + chunkMap.put(chunkCoordinate, chunk); + if (!loadInPoint.contains(chunkCoordinate)) { + chunk.scheduleGrowTask(this, -1); + } + } + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + Log.info("Error at " + file.getAbsolutePath()); + } finally { + if (delete) { + file.delete(); } } - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - Log.info("Error at " + file.getAbsolutePath()); - } finally { - if (delete) { - file.delete(); - } + } + return; + } + if (ConfigManager.updateDuringLoading) { + CCChunk chunk = chunkMap.get(chunkCoordinate); + if (chunk != null) { + chunk.executeReplaceTask(); } } } @@ -436,55 +443,70 @@ public class CCWorld extends Function { removePotData(simpleLocation); } + PotConfig potConfig = pot.getConfig(); + if (wet && fertilizer == null && !potConfig.enableFertilizedLooks()) { + return; + } + + if (world.isChunkLoaded(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4)) { + replacePot(simpleLocation, pot, potConfig); + return; + } + + if (ConfigManager.updateDuringLoading) { + addOfflineReplaceTask(simpleLocation, new OfflineReplaceTask(pot.getPotKey(), ItemType.POT, ItemMode.NOTE_BLOCK)); + return; + } + Location location = simpleLocation.getBukkitLocation(); if (location == null) { return; } - PotConfig potConfig = pot.getConfig(); - if (wet && fertilizer == null && !potConfig.isEnableFertilized()) { - return; - } - CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); - asyncGetChunk.whenComplete((result, throwable) -> - plugin.getScheduler().runTask(() -> { - Block block = location.getBlock(); - if (block.getType() == Material.AIR) { - removePotData(simpleLocation); - return; - } - String replacer = wet ? potConfig.getWetPot(fertilizer) : potConfig.getDryPot(fertilizer); - String id = plugin.getPlatformInterface().getBlockID(block); - if (ConfigManager.enableCorruptionFixer && id.equals("NOTE_BLOCK")) { - corruptedPot.put(simpleLocation, pot.getPotKey()); - if (ConfigManager.debugCorruption) AdventureUtils.consoleMessage("[CustomCrops] Corrupted pot found at: " + simpleLocation); - //plugin.getPlatformInterface().placeNoteBlock(location, replacer); - return; - } - String potKey = plugin.getPotManager().getPotKeyByBlockID(id); - if (potKey == null) { - removePotData(simpleLocation); - return; - } - if (!potKey.equals(pot.getPotKey())) { - return; - } - if (ConfigUtils.isVanillaItem(replacer)) { - block.setType(Material.valueOf(replacer)); - if (block.getBlockData() instanceof Farmland farmland && ConfigManager.disableMoistureMechanic) { - farmland.setMoisture(wet ? farmland.getMaximumMoisture() : 0); - block.setBlockData(farmland); - } - } else { - plugin.getPlatformInterface().placeNoteBlock(location, replacer); - } - } - )); + asyncGetChunk.whenComplete((result, throwable) -> { + replacePot(simpleLocation, pot, potConfig); + }); } } } + private void replacePot(SimpleLocation simpleLocation, Pot pot, PotConfig potConfig) { + Location location = simpleLocation.getBukkitLocation(); + assert location != null; + plugin.getScheduler().runTask(() -> { + Block block = location.getBlock(); + if (block.getType() == Material.AIR) { + removePotData(simpleLocation); + return; + } + String replacer = pot.isWet() ? potConfig.getWetPot(pot.getFertilizer()) : potConfig.getDryPot(pot.getFertilizer()); + String id = plugin.getPlatformInterface().getBlockID(block); + if (ConfigManager.enableCorruptionFixer && id.equals("NOTE_BLOCK")) { + corruptedPot.put(simpleLocation, pot.getPotKey()); + if (ConfigManager.debugCorruption) AdventureUtils.consoleMessage("[CustomCrops] Corrupted pot found at: " + simpleLocation); + return; + } + String potKey = plugin.getPotManager().getPotKeyByBlockID(id); + if (potKey == null) { + removePotData(simpleLocation); + return; + } + if (!potKey.equals(pot.getPotKey())) { + return; + } + if (ConfigUtils.isVanillaItem(replacer)) { + block.setType(Material.valueOf(replacer)); + if (block.getBlockData() instanceof Farmland farmland && ConfigManager.disableMoistureMechanic) { + farmland.setMoisture(pot.isWet() ? farmland.getMaximumMoisture() : 0); + block.setBlockData(farmland); + } + } else { + plugin.getPlatformInterface().placeNoteBlock(location, replacer); + } + }); + } + public class SprinklerCheckTask implements Runnable { private final SimpleLocation simpleLocation; @@ -510,12 +532,12 @@ public class CCWorld extends Function { } SprinklerAnimation sprinklerAnimation = sprinklerConfig.getSprinklerAnimation(); - Location location = simpleLocation.getBukkitLocation(); - if (location != null && sprinklerAnimation != null) { + + if (world.isChunkLoaded(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4) && sprinklerAnimation != null) { for (Player player : Bukkit.getOnlinePlayers()) { SimpleLocation playerLoc = SimpleLocation.getByBukkitLocation(player.getLocation()); if (playerLoc.isNear(simpleLocation, 48)) { - FakeEntityUtils.playWaterAnimation(player, location.clone().add(0.5, sprinklerAnimation.offset(), 0.5), sprinklerAnimation.id(), sprinklerAnimation.duration(), sprinklerAnimation.itemMode()); + FakeEntityUtils.playWaterAnimation(player, simpleLocation.getBukkitLocation().add(0.5, sprinklerAnimation.offset(), 0.5), sprinklerAnimation.id(), sprinklerAnimation.duration(), sprinklerAnimation.itemMode()); } } } @@ -549,50 +571,60 @@ public class CCWorld extends Function { @Override public void run() { - Location location = simpleLocation.getBukkitLocation(); - if (location == null) return; - CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); - asyncGetChunk.whenComplete((result, throwable) -> - plugin.getScheduler().runTask(() -> { - String blockID = plugin.getPlatformInterface().getBlockID(location.getBlock()); - String potKey = plugin.getPotManager().getPotKeyByBlockID(blockID); - if (potKey != null) { - if (whitelist != null) { - for (String pot : whitelist) { - if (pot.equals(potKey)) { - addWaterToPot(simpleLocation, amount, potKey); - break; - } - } - } else { + + if (world.isChunkLoaded(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4)) { + replacePot(simpleLocation, amount, whitelist); + return; + } + + Pot pot = getPotData(simpleLocation); + if (ConfigManager.updateDuringLoading && pot != null) { + pot.addWater(amount); + addOfflineReplaceTask(simpleLocation, new OfflineReplaceTask("unknown", ItemType.POT, ItemMode.NOTE_BLOCK)); + return; + } + + CompletableFuture asyncGetChunk = world.getChunkAtAsync(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4); + asyncGetChunk.whenComplete((result, throwable) -> replacePot(simpleLocation, amount, whitelist)); + } + } + + private void replacePot(SimpleLocation simpleLocation, int amount, String[] whitelist) { + Location location = simpleLocation.getBukkitLocation(); + assert location != null; + plugin.getScheduler().runTask(() -> { + String blockID = plugin.getPlatformInterface().getBlockID(location.getBlock()); + String potKey = plugin.getPotManager().getPotKeyByBlockID(blockID); + if (potKey != null) { + if (whitelist != null) { + for (String pot : whitelist) { + if (pot.equals(potKey)) { addWaterToPot(simpleLocation, amount, potKey); - } - } else if (ConfigManager.enableCorruptionFixer && blockID.equals("NOTE_BLOCK")) { - Pot pot = getPotData(simpleLocation); - if (pot != null) { - // mark it as corrupted - potKey = pot.getPotKey(); - if (whitelist == null) { - pot.addWater(amount); - } else { - for (String potID : whitelist) { - if (potID.equals(potKey)) { - pot.addWater(amount); - break; - } - } - } - corruptedPot.put(simpleLocation, potKey); - if (ConfigManager.debugCorruption) AdventureUtils.consoleMessage("[CustomCrops] Corrupted pot found at: " + simpleLocation); - // only custom blocks would corrupt - // so it's not necessary to check if the pot is a vanilla block - // String replacer = pot.isWet() ? pot.getConfig().getWetPot(pot.getFertilizer()) : pot.getConfig().getDryPot(pot.getFertilizer()); - // plugin.getPlatformInterface().placeNoteBlock(location, replacer); + break; } } + } else { + addWaterToPot(simpleLocation, amount, potKey); } - )); - } + } else if (ConfigManager.enableCorruptionFixer && blockID.equals("NOTE_BLOCK")) { + Pot pot = getPotData(simpleLocation); + if (pot != null) { + potKey = pot.getPotKey(); + if (whitelist == null) { + pot.addWater(amount); + } else { + for (String potID : whitelist) { + if (potID.equals(potKey)) { + pot.addWater(amount); + break; + } + } + } + corruptedPot.put(simpleLocation, potKey); + if (ConfigManager.debugCorruption) AdventureUtils.consoleMessage("[CustomCrops] Corrupted pot found at: " + simpleLocation); + } + } + }); } public class CropCheckTask implements Runnable { @@ -689,49 +721,68 @@ public class CCWorld extends Function { String finalNextModel = nextModel; if (finalNextModel == null || location == null) return; + if (world.isChunkLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4)) { + switch (itemMode) { + case ITEM_FRAME -> replaceItemFrameCrop(location, finalNextModel, cropConfig.isRotationEnabled()); + case ITEM_DISPLAY -> replaceItemDisplayCrop(location, finalNextModel, cropConfig.isRotationEnabled()); + case TRIPWIRE -> replaceTripwireCrop(location, finalNextModel); + } + return; + } + + if (ConfigManager.updateDuringLoading) { + addOfflineReplaceTask(simpleLocation, new OfflineReplaceTask(finalNextModel, ItemType.CROP, itemMode)); + return; + } + CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); if (itemMode == ItemMode.ITEM_FRAME) { CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { chunk.getEntities(); return chunk.isEntitiesLoaded(); }); - loadEntities.whenComplete((result, throwable) -> - plugin.getScheduler().runTask(() -> { - if (plugin.getPlatformInterface().removeCustomItem(location, itemMode)) { - ItemFrame itemFrame = plugin.getPlatformInterface().placeItemFrame(location, finalNextModel); - if (itemFrame != null && cropConfig.isRotationEnabled()) itemFrame.setRotation(RotationUtils.getRandomRotation()); - } else { - removeCropData(simpleLocation); - } - })); + loadEntities.whenComplete((result, throwable) -> replaceItemFrameCrop(location, finalNextModel, cropConfig.isRotationEnabled())); } else if (itemMode == ItemMode.ITEM_DISPLAY) { CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { chunk.getEntities(); return chunk.isEntitiesLoaded(); }); - loadEntities.whenComplete((result, throwable) -> - plugin.getScheduler().runTask(() -> { - if (plugin.getPlatformInterface().removeCustomItem(location, itemMode)) { - ItemDisplay itemDisplay = plugin.getPlatformInterface().placeItemDisplay(location, finalNextModel); - if (itemDisplay != null && cropConfig.isRotationEnabled()) itemDisplay.setRotation(RotationUtils.getRandomFloatRotation(), itemDisplay.getLocation().getPitch()); - } else { - removeCropData(simpleLocation); - } - })); + loadEntities.whenComplete((result, throwable) -> replaceItemDisplayCrop(location, finalNextModel, cropConfig.isRotationEnabled())); } else { - asyncGetChunk.whenComplete((result, throwable) -> - plugin.getScheduler().runTask(() -> { - if (plugin.getPlatformInterface().removeCustomItem(location, itemMode)) { - plugin.getPlatformInterface().placeTripWire(location, finalNextModel); - } else { - removeCropData(simpleLocation); - } - })); + asyncGetChunk.whenComplete((result, throwable) -> replaceTripwireCrop(location, finalNextModel)); } } - public World getWorld() { - return world.get(); + private void replaceItemFrameCrop(Location location, String model, boolean rotation) { + plugin.getScheduler().runTask(() -> { + if (plugin.getPlatformInterface().removeCustomItem(location, ItemMode.ITEM_FRAME)) { + ItemFrame itemFrame = plugin.getPlatformInterface().placeItemFrame(location, model); + if (itemFrame != null && rotation) itemFrame.setRotation(RotationUtils.getRandomRotation()); + } else { + removeCropData(SimpleLocation.getByBukkitLocation(location)); + } + }); + } + + private void replaceItemDisplayCrop(Location location, String model, boolean rotation) { + plugin.getScheduler().runTask(() -> { + if (plugin.getPlatformInterface().removeCustomItem(location, ItemMode.ITEM_DISPLAY)) { + ItemDisplay itemDisplay = plugin.getPlatformInterface().placeItemDisplay(location, model); + if (itemDisplay != null && rotation) itemDisplay.setRotation(RotationUtils.getRandomFloatRotation(), itemDisplay.getLocation().getPitch()); + } else { + removeCropData(SimpleLocation.getByBukkitLocation(location)); + } + }); + } + + private void replaceTripwireCrop(Location location, String model) { + plugin.getScheduler().runTask(() -> { + if (plugin.getPlatformInterface().removeCustomItem(location, ItemMode.TRIPWIRE)) { + plugin.getPlatformInterface().placeTripWire(location, model); + } else { + removeCropData(SimpleLocation.getByBukkitLocation(location)); + } + }); } public void removePotData(SimpleLocation simpleLocation) { @@ -884,8 +935,9 @@ public class CCWorld extends Function { } public CCChunk createNewChunk(SimpleLocation simpleLocation) { + ChunkCoordinate chunkCoordinate = simpleLocation.getChunkCoordinate(); CCChunk newChunk = new CCChunk(); - chunkMap.put(simpleLocation.getChunkCoordinate(), newChunk); + chunkMap.put(chunkCoordinate, newChunk); return newChunk; } @@ -913,6 +965,16 @@ public class CCWorld extends Function { }); } + public void addOfflineReplaceTask(SimpleLocation simpleLocation, OfflineReplaceTask offlineReplaceTask) { + CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); + if (chunk != null) { + chunk.addReplaceTask(simpleLocation, offlineReplaceTask); + return; + } + chunk = createNewChunk(simpleLocation); + chunk.addReplaceTask(simpleLocation, offlineReplaceTask); + } + @Nullable public String getCorruptedPotOriginalKey(SimpleLocation simpleLocation) { return corruptedPot.get(simpleLocation); diff --git a/src/main/java/net/momirealms/customcrops/api/object/world/WorldDataManager.java b/src/main/java/net/momirealms/customcrops/api/object/world/WorldDataManager.java index 6ddde9c..9897305 100644 --- a/src/main/java/net/momirealms/customcrops/api/object/world/WorldDataManager.java +++ b/src/main/java/net/momirealms/customcrops/api/object/world/WorldDataManager.java @@ -19,6 +19,7 @@ package net.momirealms.customcrops.api.object.world; import net.momirealms.customcrops.CustomCrops; import net.momirealms.customcrops.api.object.Function; +import net.momirealms.customcrops.api.object.OfflineReplaceTask; import net.momirealms.customcrops.api.object.basic.ConfigManager; import net.momirealms.customcrops.api.object.crop.GrowingCrop; import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; @@ -228,6 +229,13 @@ public class WorldDataManager extends Function { } } + public void addOfflineTask(SimpleLocation simpleLocation, OfflineReplaceTask offlineReplaceTask) { + CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); + if (ccWorld != null) { + ccWorld.addOfflineReplaceTask(simpleLocation, offlineReplaceTask); + } + } + public void addWaterToSprinkler(SimpleLocation simpleLocation, int add, SprinklerConfig sprinklerConfig) { Sprinkler sprinkler = getSprinklerData(simpleLocation); if (sprinkler != null) { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index cb3fecf..0dbcdef 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,5 @@ # Don't change -config-version: '33' +config-version: '34' # BStats metrics: true # Language: english / spanish / chinese / turkish / russian @@ -67,6 +67,12 @@ optimization: # 指定世界的设置 worlds: - world:64 + # Experimental option (Only works when only-work-in-loaded-chunks is false) + # This option would make the plugin no longer invoke the unloaded chunks to replace crop models/pots + # Crops/Pots would only be updated during the chunk loading + # 实验性选项,启用以后农作物/种植盆的模型只会在区块加载时得到更新,不再异步加载区块替换模型 + # 此选项仅在only-work-in-loaded-chunks为false时候生效 + only-update-during-chunk-loading: false mechanics: # Does the system only work in loaded chunks (Requires you to stop the server before changing this setting) @@ -74,6 +80,7 @@ mechanics: only-work-in-loaded-chunks: true # 17/2/1 = 85%/10%/5% # 2/2/1 = 40%/40%/20% + # You can customize more ranks like x/x/x/x/x default-quality-ratio: 17/2/1 # Season mechanic # 季节机制