9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-19 15:09:25 +00:00
This commit is contained in:
Xiao-MoMi
2023-04-29 15:36:37 +08:00
parent 17ad463d53
commit 802a9ab5f8
14 changed files with 173 additions and 136 deletions

View File

@@ -13,45 +13,3 @@ https://polymart.org/resource/customcrops.2625
### How to compile
Just compile it with gradle build. Some premium plugins are used as
local libraries. If you don't need those integrations just remove them!
### Game Mechanics
Crops will start growing at about 7am (Game Time).\
Crops in watered pot would definitely grow a stage every day while those in dry pot have less chance.
### Season & Greenhouse
Season is now an important part of StardewValley Farming System.
Crops only grow in a suitable season and inproper
seasons will make crops into a dead stage.
You can use greenhouse glass to allow them to grow all year.
### Fertilizer
There are five templates of fertiziliers: \
SpeedGrow: Crops have a small chance to grow two stages at a time\
RetainingSoil: Pot have a small chance to retain its water after crops grow\
QuailityCrops: When haveresting, players have a higher chance to get high quality crops.\
YieldIncreasing: When haveresting, players have a higher chance to get more crops.\
Gigantic: Crops have a higher chance to be gigantic.
### Sprinkler & WateringCan
Sprinkler is a semi-automatic way of watering pot. You can add water to sprinkler with
water bucket or watering can. Max storage and range can be customized.\
Watering-can also has its max storage and effective range. 1x1 1x3 3x3 and even 9x99 is supported!
### OverWeight
If configurated, crops have a very little chance to be OverWeight(gigantic) after they are ripe.
### Quality
Crops have three qualities, if you don't want this feature just delete it in config.
Quality is determined by the fertizilier players use and their luck!
### Harvest Repeatedly
If configurated, crops can be harvested repeatedly and return to a specified stage.\
This is useful for crops like grape.
### Crow and Scarecrow
Crops have a little chance to be eaten by a crow.\
A scarecrow would help the crops free of their attacks in one chunk.
### Highly Optimizied
Crops growing progress is distributed evenly to the whole day so there would not be large amount of blocks replacement at the same time.\
Crops' data would be removed from file as soon as they are ripe, which would not affect your server performance in the long term.

View File

@@ -4,7 +4,7 @@ plugins {
}
group = 'net.momirealms'
version = '3.0.5'
version = '3.0.6'
repositories {
mavenCentral()

View File

@@ -33,7 +33,6 @@ import net.momirealms.customcrops.api.object.fertilizer.FertilizerManager;
import net.momirealms.customcrops.api.object.hologram.HologramManager;
import net.momirealms.customcrops.api.object.pot.PotManager;
import net.momirealms.customcrops.api.object.scheduler.Scheduler;
import net.momirealms.customcrops.api.object.season.CCSeason;
import net.momirealms.customcrops.api.object.season.SeasonManager;
import net.momirealms.customcrops.api.object.sprinkler.SprinklerManager;
import net.momirealms.customcrops.api.object.wateringcan.WateringCanManager;

View File

@@ -17,8 +17,6 @@
package net.momirealms.customcrops.api.customplugin;
import io.th0rgal.oraxen.api.OraxenFurniture;
import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic;
import net.momirealms.customcrops.CustomCrops;
import net.momirealms.customcrops.api.object.ItemMode;
import org.bukkit.Location;

View File

@@ -61,10 +61,7 @@ import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.*;
public class PlatformManager extends Function {
@@ -307,10 +304,8 @@ public class PlatformManager extends Function {
Bukkit.getPluginManager().callEvent(potPlaceEvent);
if (potPlaceEvent.isCancelled()) {
if (event != null) event.setCancelled(true);
return true;
}
plugin.getWorldDataManager().addPotData(SimpleLocation.getByBukkitLocation(location), new Pot(potConfig.getKey(), null, 0));
return true;
}
@@ -659,7 +654,7 @@ public class PlatformManager extends Function {
if (player.getGameMode() != GameMode.CREATIVE) item_in_hand.setAmount(item_in_hand.getAmount() - 1);
player.swingMainHand();
CustomCrops.getInstance().getPlatformInterface().placeCustomItem(crop_loc, cropPlantEvent.getCropModel(), cropConfig.getCropMode());
plugin.getWorldDataManager().addCropData(SimpleLocation.getByBukkitLocation(crop_loc), new GrowingCrop(cropConfig.getKey(), cropPlantEvent.getPoint()));
plugin.getWorldDataManager().addCropData(SimpleLocation.getByBukkitLocation(crop_loc), new GrowingCrop(cropConfig.getKey(), cropPlantEvent.getPoint()), true);
return true;
}
@@ -740,9 +735,9 @@ public class PlatformManager extends Function {
}
}
Pot potData = plugin.getWorldDataManager().getPotData(SimpleLocation.getByBukkitLocation(location));
Pot potData = Optional.ofNullable(plugin.getWorldDataManager().getPotData(SimpleLocation.getByBukkitLocation(location))).orElse(new Pot(pot_id, null, 0));
GrowingCrop growingCrop = plugin.getWorldDataManager().getCropData(SimpleLocation.getByBukkitLocation(location));
PotInfoEvent potInfoEvent = new PotInfoEvent(player, location, item_in_hand, potConfig, potData == null ? null : potData.getFertilizer(), potData == null ? 0 : potData.getWater(), growingCrop);
PotInfoEvent potInfoEvent = new PotInfoEvent(player, location, item_in_hand, potConfig, potData.getFertilizer(), potData.getWater(), growingCrop);
Bukkit.getPluginManager().callEvent(potInfoEvent);
if (potConfig.getRequiredItem() != null && !item_in_hand_id.equals(potConfig.getRequiredItem())) {
@@ -750,7 +745,7 @@ public class PlatformManager extends Function {
}
WaterAmountHologram waterAmountHologram = potConfig.getWaterAmountHologram();
if (waterAmountHologram != null && potData != null) {
if (waterAmountHologram != null) {
double offset = 0;
StageConfig stageConfig = plugin.getCropManager().getStageConfig(plugin.getPlatformInterface().getAnyItemIDAt(location.clone().add(0,1,0)));
if (stageConfig != null) {
@@ -766,7 +761,7 @@ public class PlatformManager extends Function {
}
FertilizerHologram fertilizerHologram = potConfig.getFertilizerHologram();
if (fertilizerHologram != null && potData != null && potData.getFertilizer() != null) {
if (fertilizerHologram != null && potData.getFertilizer() != null) {
double offset = 0;
StageConfig stageConfig = plugin.getCropManager().getStageConfig(plugin.getPlatformInterface().getAnyItemIDAt(location.clone().add(0,1,0)));
if (stageConfig != null) {

View File

@@ -55,7 +55,7 @@ public class ReplantImpl implements Action {
}
if (!CustomCrops.getInstance().getPlatformInterface().detectAnyThing(location)) {
CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, model, newCMode);
CustomCrops.getInstance().getWorldDataManager().addCropData(crop_loc, new GrowingCrop(crop, point));
CustomCrops.getInstance().getWorldDataManager().addCropData(crop_loc, new GrowingCrop(crop, point), false);
}
return null;
});

View File

@@ -61,6 +61,8 @@ public class ConfigManager extends Function {
public static int maxCropPerChunk;
public static int cacheSaveInterval;
public static boolean setUpMode;
public static int intervalConsume;
public static int intervalWork;
private final HashMap<String, Integer> cropPerWorld;
private final CustomCrops plugin;
@@ -115,12 +117,14 @@ public class ConfigManager extends Function {
}
private void loadScheduleSystem(ConfigurationSection section) {
enableScheduleSystem = section.getBoolean("default-schedule");
pointGainInterval = section.getInt("point-gain-interval", 1000);
enableScheduleSystem = section.getBoolean("others.enable", true);
pointGainInterval = section.getInt("point-gain-interval", 600);
corePoolSize = section.getInt("thread-pool-settings.corePoolSize", 2);
maxPoolSize = section.getInt("thread-pool-settings.maximumPoolSize", 4);
keepAliveTime = section.getInt("thread-pool-settings.keepAliveTime", 10);
cacheSaveInterval = section.getInt("cache-save-interval", 7200);
cacheSaveInterval = section.getInt("cache-save-interval", 12000);
intervalConsume = section.getInt("others.consume-water-fertilizer-every-x-point", 2);
intervalWork = section.getInt("others.sprinkler-work-every-x-point", 2);
}
private void loadMechanic(ConfigurationSection section) {

View File

@@ -17,9 +17,7 @@
package net.momirealms.customcrops.api.object.migrate;
import net.momirealms.customcrops.CustomCrops;
import net.momirealms.customcrops.api.object.Function;
import net.momirealms.customcrops.api.object.basic.ConfigManager;
import net.momirealms.customcrops.api.object.crop.GrowingCrop;
import net.momirealms.customcrops.api.object.fertilizer.Fertilizer;
import net.momirealms.customcrops.api.object.pot.Pot;

View File

@@ -133,8 +133,19 @@ public class CCChunk implements Serializable {
return null;
});
}
return;
}
else if (pot_id != null) {
if (pot_id == null) {
Location bukkitLoc = simpleLocation.getBukkitLocation();
if (bukkitLoc == null) return;
String id = CustomCrops.getInstance().getPlatformInterface().getCustomBlockID(bukkitLoc);
if (id != null) {
pot_id = CustomCrops.getInstance().getPotManager().getPotKeyByBlockID(id);
} else {
return;
}
}
Pot newPot = new Pot(pot_id, null, amount);
potMap.put(simpleLocation, newPot);
CustomCrops.getInstance().getScheduler().callSyncMethod(() -> {
@@ -142,7 +153,6 @@ public class CCChunk implements Serializable {
return null;
});
}
}
public void addFertilizerToPot(SimpleLocation simpleLocation, Fertilizer fertilizer, @NotNull String pot_id) {
Pot pot = potMap.get(simpleLocation);
@@ -172,15 +182,17 @@ public class CCChunk implements Serializable {
public void scheduleSprinklerTask(CCWorld ccWorld) {
Random randomGenerator = ThreadLocalRandom.current();
int delay = Math.min(30, ConfigManager.pointGainInterval);
for (SimpleLocation simpleLocation : sprinklerMap.keySet()) {
ccWorld.pushSprinklerTask(simpleLocation, randomGenerator.nextInt(30));
ccWorld.pushSprinklerTask(simpleLocation, randomGenerator.nextInt(delay));
}
}
public void scheduleConsumeTask(CCWorld ccWorld) {
Random randomGenerator = ThreadLocalRandom.current();
int delay = Math.min(30, ConfigManager.pointGainInterval);
for (SimpleLocation simpleLocation : potMap.keySet()) {
ccWorld.pushConsumeTask(simpleLocation, randomGenerator.nextInt(60));
ccWorld.pushConsumeTask(simpleLocation, randomGenerator.nextInt(delay));
}
}

View File

@@ -57,6 +57,9 @@ import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
@@ -69,11 +72,13 @@ public class CCWorld extends Function {
private final GenericObjectPool<CropCheckTask> cropTaskPool;
private final GenericObjectPool<SprinklerCheckTask> sprinklerTaskPool;
private final GenericObjectPool<ConsumeCheckTask> consumeTaskPool;
private long lastWorkDay;
private long lastConsumeDay;
private long current_day;
private ScheduledFuture<?> timerTask;
private int timer;
private int pointTimer;
private int cacheTimer;
private int workCounter;
private int consumeCounter;
private final HashSet<SimpleLocation> plantToday;
public CCWorld(World world) {
this.world = new WeakReference<>(world);
@@ -92,7 +97,8 @@ public class CCWorld extends Function {
this.consumeTaskPool = new GenericObjectPool<>(new ConsumeTaskFactory(), new GenericObjectPoolConfig<>());
this.consumeTaskPool.setMaxTotal(10);
this.consumeTaskPool.setMinIdle(1);
this.timer = 10;
this.plantToday = new HashSet<>(128);
this.pointTimer = 10;
this.cacheTimer = ConfigManager.cacheSaveInterval;
}
@@ -103,12 +109,14 @@ public class CCWorld extends Function {
if (!chunks_folder.exists()) chunks_folder.mkdirs();
File[] data_files = chunks_folder.listFiles();
if (data_files == null) return;
List<File> outdated = new ArrayList<>();
for (File file : data_files) {
ChunkCoordinate chunkCoordinate = ChunkCoordinate.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();
if (chunk.isUseless()) {
file.delete();
outdated.add(file);
continue;
}
if (chunkCoordinate != null) chunkMap.put(chunkCoordinate, chunk);
@@ -116,6 +124,11 @@ public class CCWorld extends Function {
e.printStackTrace();
}
}
for (File file : outdated) {
file.delete();
}
YamlConfiguration dataFile;
if (ConfigManager.worldFolderPath.equals("")) {
dataFile = ConfigUtils.readData(new File(CustomCrops.getInstance().getDataFolder().getParentFile().getParentFile(), worldName + File.separator + "customcrops" + File.separator + "data.yml"));
@@ -131,19 +144,19 @@ public class CCWorld extends Function {
}
CustomCrops.getInstance().getSeasonManager().loadSeasonData(seasonData);
}
this.lastConsumeDay = dataFile.getLong("last-consume-day", 0);
this.lastWorkDay = dataFile.getLong("last-work-day", 0);
this.current_day = dataFile.getLong("day", 0);
}
@Override
public void disable() {
closePool();
saveCache();
saveCrop();
saveDate();
CustomCrops.getInstance().getSeasonManager().unloadSeasonData(worldName);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void saveCache() {
public void saveCrop() {
File chunks_folder = ConfigUtils.getChunkFolder(worldName);
if (!chunks_folder.exists()) chunks_folder.mkdirs();
for (Map.Entry<ChunkCoordinate, CCChunk> entry : chunkMap.entrySet()) {
@@ -161,6 +174,9 @@ public class CCWorld extends Function {
e.printStackTrace();
}
}
}
public void saveDate() {
YamlConfiguration dataFile = new YamlConfiguration();
if (ConfigManager.enableSeason && !ConfigManager.rsHook) {
SeasonData seasonData = CustomCrops.getInstance().getSeasonManager().getSeasonData(worldName);
@@ -172,8 +188,7 @@ public class CCWorld extends Function {
dataFile.set("date", seasonData.getDate());
}
}
dataFile.set("last-consume-day", lastConsumeDay);
dataFile.set("last-work-day", lastWorkDay);
dataFile.set("day", current_day);
try {
dataFile.save(new File(CustomCrops.getInstance().getDataFolder().getParentFile().getParentFile(), ConfigManager.worldFolderPath + worldName + File.separator + "customcrops" + File.separator + "data.yml"));
} catch (IOException e) {
@@ -182,52 +197,86 @@ public class CCWorld extends Function {
}
public void load() {
timer = ConfigManager.pointGainInterval;
this.pointTimer = ConfigManager.pointGainInterval;
this.cacheTimer = ConfigManager.cacheSaveInterval;
this.consumeCounter = ConfigManager.intervalConsume;
this.workCounter = ConfigManager.intervalWork;
this.scheduleTask();
}
private void scheduleTask() {
if (this.timerTask == null) {
this.timerTask = CustomCrops.getInstance().getScheduler().runTaskTimerAsync(() -> {
if (ConfigManager.debug) Log.info("Task queue size: " + schedule.getQueue().size());
World current = world.get();
if (current != null) {
long fullTime = current.getFullTime();
long day = fullTime / 24000;
if (ConfigManager.debug) Log.info("Task queue size: " + schedule.getQueue().size());
long day = current.getFullTime() / 24000;
long time = current.getTime();
if (time < 60 && day != lastConsumeDay) {
lastConsumeDay = day;
this.tryDayCycleTask(time, day);
this.tryScheduleGrow();
}
else {
AdventureUtils.consoleMessage("<red>[CustomCrops] World: " + worldName + " unloaded unexpectedly. Shutdown the schedule.");
this.schedule.shutdown();
}
}, 1000, 1000L);
}
}
private void tryDayCycleTask(long time, long day) {
if (time < 100 && day != current_day) {
current_day = day;
if (ConfigManager.enableSeason && !ConfigManager.rsHook && ConfigManager.autoSeasonChange) {
CustomCrops.getInstance().getSeasonManager().addDate(worldName);
}
if (ConfigManager.enableScheduleSystem) {
scheduleConsumeTask();
}
}
else if (time > 970 && time < 1030 && lastWorkDay != day) {
lastWorkDay = day;
if (ConfigManager.enableScheduleSystem) {
scheduleSprinklerWork();
}
}
timer--;
if (timer <= 0) {
if (ConfigManager.debug) Log.info("== Grow point ==");
timer = ConfigManager.pointGainInterval;
for (CCChunk chunk : chunkMap.values()) {
chunk.scheduleGrowTask(this);
}
}
if (ConfigManager.cacheSaveInterval != -1) {
cacheTimer--;
if (cacheTimer <= 0) {
if (ConfigManager.debug) Log.info("== Save cache ==");
cacheTimer = ConfigManager.cacheSaveInterval;
schedule.execute(this::saveCache);
schedule.execute(this::saveDate);
}
}
}
else {
AdventureUtils.consoleMessage("<red>[CustomCrops] Unexpected world: " + worldName + " unloaded. Shutdown the schedule.");
this.schedule.shutdown();
private void tryScheduleGrow() {
pointTimer--;
if (pointTimer <= 0) {
pointTimer = ConfigManager.pointGainInterval;
plantToday.clear();
if (ConfigManager.debug) Log.info("== Grow point ==");
int size = schedule.getQueue().size();
if (size != 0) {
schedule.getQueue().clear();
if (ConfigManager.debug) Log.info("== Clear queue ==");
}
for (CCChunk chunk : chunkMap.values()) {
chunk.scheduleGrowTask(this);
}
if (ConfigManager.enableScheduleSystem) {
workCounter--;
consumeCounter--;
if (consumeCounter <= 0) {
consumeCounter = ConfigManager.intervalConsume;
if (ConfigManager.debug) Log.info("== Consume time ==");
scheduleConsumeTask();
}
if (workCounter <= 0) {
workCounter = ConfigManager.intervalWork;
if (ConfigManager.debug) Log.info("== Work time ==");
scheduleSprinklerWork();
}
}
}, 1000, 1000L);
}
}
@@ -275,7 +324,7 @@ public class CCWorld extends Function {
CropCheckTask cropCheckTask = cropTaskPool.borrowObject();
GrowingCrop growingCrop = getCropData(simpleLocation);
if (growingCrop == null) return;
cropCheckTask.setArgs(simpleLocation, growingCrop);
cropCheckTask.setArgs(simpleLocation.copy(), growingCrop);
cropCheckTask.execute();
cropTaskPool.returnObject(cropCheckTask);
}
@@ -345,11 +394,20 @@ public class CCWorld extends Function {
pot.setWater(pot.getWater() + 1);
}
if (pot.reduceWater() | pot.reduceFertilizer()) {
PotConfig potConfig = pot.getConfig();
Fertilizer fertilizer = pot.getFertilizer();
boolean wet = pot.isWet();
if (!wet && fertilizer == null) {
removePotData(simpleLocation);
}
Location location = simpleLocation.getBukkitLocation();
if (location == null) return;
if (location == null) {
return;
}
CustomCrops.getInstance().getScheduler().callSyncMethod(() -> {
if (CustomCrops.getInstance().getPlatformInterface().removeAnyBlock(location)) {
String replacer = wet ? potConfig.getWetPot(fertilizer) : potConfig.getDryPot(fertilizer);
@@ -432,7 +490,6 @@ public class CCWorld extends Function {
}
public void execute() {
CropConfig cropConfig = growingCrop.getConfig();
if (cropConfig == null) {
removeCropData(simpleLocation);
@@ -631,14 +688,24 @@ public class CCWorld extends Function {
chunk.removeCropData(simpleLocation);
}
public void addCropData(SimpleLocation simpleLocation, GrowingCrop growingCrop) {
public void addCropData(SimpleLocation simpleLocation, GrowingCrop growingCrop, boolean grow) {
CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate());
if (chunk != null) {
chunk.addCropData(simpleLocation, growingCrop);
if (grow) growIfNotDuplicated(simpleLocation);
return;
}
chunk = createNewChunk(simpleLocation);
chunk.addCropData(simpleLocation, growingCrop);
if (grow) growIfNotDuplicated(simpleLocation);
}
private void growIfNotDuplicated(SimpleLocation simpleLocation) {
if (plantToday.contains(simpleLocation)) {
return;
}
pushCropTask(simpleLocation, ThreadLocalRandom.current().nextInt(ConfigManager.pointGainInterval));
plantToday.add(simpleLocation);
}
public GrowingCrop getCropData(SimpleLocation simpleLocation) {

View File

@@ -91,10 +91,10 @@ public class WorldDataManager extends Function {
}
}
public void addCropData(SimpleLocation simpleLocation, GrowingCrop growingCrop) {
public void addCropData(SimpleLocation simpleLocation, GrowingCrop growingCrop, boolean grow) {
CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName());
if (ccWorld != null) {
ccWorld.addCropData(simpleLocation, growingCrop);
ccWorld.addCropData(simpleLocation, growingCrop, grow);
}
}

View File

@@ -30,7 +30,7 @@
// for (int j = 0; j < radius * 2; j++) {
// for (int y = 0; y < 10; y++) {
// SimpleLocation temp = simpleLocation1.add(i, y, j);
// worldDataManager.addCropData(temp, new GrowingCrop("tomato", 0));
// worldDataManager.addCropData(temp, new GrowingCrop("tomato", 0), false);
// }
// }
// }

View File

@@ -19,7 +19,6 @@ package net.momirealms.customcrops.integration.job;
import com.willfp.ecojobs.api.EcoJobsAPI;
import com.willfp.ecojobs.jobs.Job;
import com.willfp.ecojobs.jobs.Jobs;
import net.momirealms.customcrops.integration.JobInterface;
import org.bukkit.entity.Player;

View File

@@ -1,5 +1,5 @@
# Don't change
config-version: '27'
config-version: '28'
# BStats
metrics: true
# Language: english / spanish / chinese / turkish
@@ -20,19 +20,26 @@ worlds:
- world
schedule-system:
# Water amount and fertilizer use would be reduced at 6am
# Sprinklers would work at 7am
# if disabled, you can do that manually with command /customcrops force [consume/sprinklerwork]
# 水分和肥料将在每天6点减少洒水器将在7点工作
# 如果禁用,你可以使用指令/customcrops force [consume/sprinklerwork]手动操作
default-schedule: true
# The average interval for a crop to gain one point (measured in seconds)
# 平均每个农作物获得一生长点的时间间隔(秒)
point-gain-interval: 1200
point-gain-interval: 600
others:
# if disabled, you can do that manually with command /customcrops force [consume/sprinklerwork]
# 如果禁用,你可以使用指令/customcrops force [consume/sprinklerwork]手动操作
enable: true
# Water amount and fertilizer would reduce every two points are gained
# As the default point-gain-interval is 600s, so here it would be 1200s(20min = a minecraft day)
consume-water-fertilizer-every-x-point: 2
# Sprinkler would work every two points are gained
sprinkler-work-every-x-point: 2
# Save cache to file interval (seconds)
# set "-1" to disable
# 保存缓存的时间间隔 (秒)
cache-save-interval: 7500
cache-save-interval: 12000
# Thread pool settings
# 线程池设置
thread-pool-settings: