9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-19 15:09:25 +00:00

offline ticks

This commit is contained in:
XiaoMoMi
2024-09-04 02:49:18 +08:00
parent 32f01f1aec
commit a1885e1dda
32 changed files with 216 additions and 142 deletions

View File

@@ -24,12 +24,14 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.Context;
import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.*; 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.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; 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.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.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsChunk; import net.momirealms.customcrops.api.core.world.CustomCropsChunk;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.CustomCropsWorld;

View File

@@ -21,11 +21,11 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.Context;
import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.block.*; import net.momirealms.customcrops.api.core.block.CropBlock;
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.BoneMeal;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; 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.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.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.CustomCropsWorld;

View File

@@ -12,15 +12,15 @@ import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import dev.dejvokep.boostedyaml.utils.format.NodeRole; import dev.dejvokep.boostedyaml.utils.format.NodeRole;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; 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.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.BoneMeal;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; 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.sprinkler.SprinklerConfig; 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.CustomCropsBlockState;
import net.momirealms.customcrops.api.misc.water.FillMethod; import net.momirealms.customcrops.api.misc.water.FillMethod;
import net.momirealms.customcrops.api.misc.water.WateringMethod; import net.momirealms.customcrops.api.misc.water.WateringMethod;

View File

@@ -17,13 +17,13 @@
package net.momirealms.customcrops.api.core; 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.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.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.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; 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.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.common.annotation.DoNotUse; import net.momirealms.customcrops.common.annotation.DoNotUse;
import net.momirealms.customcrops.common.util.Key; import net.momirealms.customcrops.common.util.Key;

View File

@@ -78,11 +78,11 @@ public abstract class AbstractCustomCropsBlock implements CustomCropsBlock {
} }
@Override @Override
public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) { public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
} }
@Override @Override
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) { public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
} }
@Override @Override

View File

@@ -23,9 +23,9 @@ import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.Context;
import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.*; 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.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; 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.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
@@ -58,16 +58,16 @@ public class CropBlock extends AbstractCustomCropsBlock {
} }
@Override @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())) { if (!world.setting().randomTickCrop() && canTick(state, world.setting().tickCropInterval())) {
tickCrop(state, world, location); tickCrop(state, world, location, offlineTick);
} }
} }
@Override @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())) { if (world.setting().randomTickCrop() && canTick(state, world.setting().tickCropInterval())) {
tickCrop(state, world, location); tickCrop(state, world, location, offlineTick);
} }
} }
@@ -273,20 +273,20 @@ public class CropBlock extends AbstractCustomCropsBlock {
} }
CropConfig cropConfig = configList.get(0); CropConfig cropConfig = configList.get(0);
CropStageConfig stageConfig = cropConfig.stageByID(stageID); CropStageConfig stageConfig = cropConfig.stageByID(stageID);
assert stageConfig != null;
int point = stageConfig.point(); int point = stageConfig.point();
CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState(); CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState();
point(state, point); point(state, point);
id(state, cropConfig.id()); id(state, cropConfig.id());
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
"Overwrite old data with " + state + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );
}); });
return state; 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); CropConfig config = config(state);
if (config == null) { 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."); 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 @@ public class CropBlock extends AbstractCustomCropsBlock {
} }
} }
Context<CustomCropsBlockState> context = Context.block(state); Context<CustomCropsBlockState> context = Context.block(state).arg(ContextKeys.OFFLINE, offline);
Location bukkitLocation = location.toLocation(bukkitWorld); Location bukkitLocation = location.toLocation(bukkitWorld);
context.arg(ContextKeys.LOCATION, bukkitLocation); context.arg(ContextKeys.LOCATION, bukkitLocation);
for (DeathCondition deathCondition : config.deathConditions()) { for (DeathCondition deathCondition : config.deathConditions()) {

View File

@@ -34,9 +34,9 @@ public interface CustomCropsBlock {
CustomCropsBlockState createBlockState(CompoundMap data); 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); void onInteract(WrappedInteractEvent event);

View File

@@ -40,12 +40,13 @@ public class GreenhouseBlock extends AbstractCustomCropsBlock {
} }
@Override @Override
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) { public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
//tickGreenhouse(world, location); //tickGreenhouse(world, location);
} }
@Override @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); tickGreenhouse(world, location);
} }
@@ -89,9 +90,8 @@ public class GreenhouseBlock extends AbstractCustomCropsBlock {
Pos3 pos3 = Pos3.from(event.location()); Pos3 pos3 = Pos3.from(event.location());
CustomCropsWorld<?> world = event.world(); CustomCropsWorld<?> world = event.world();
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
"Overwrite old data with " + state + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );
}); });
} }
@@ -108,7 +108,7 @@ public class GreenhouseBlock extends AbstractCustomCropsBlock {
} }
CustomCropsBlockState state = createBlockState(); CustomCropsBlockState state = createBlockState();
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() ->
"Overwrite old data with " + state + "Overwrite old data with " + state +
" at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );

View File

@@ -23,11 +23,11 @@ import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.Context;
import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.*; 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.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; 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.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.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3; import net.momirealms.customcrops.api.core.world.Pos3;
@@ -67,16 +67,16 @@ public class PotBlock extends AbstractCustomCropsBlock {
} }
@Override @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())) { if (!world.setting().randomTickPot() && canTick(state, world.setting().tickPotInterval())) {
tickPot(state, world, location); tickPot(state, world, location, offlineTick);
} }
} }
@Override @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())) { if (world.setting().randomTickPot() && canTick(state, world.setting().tickPotInterval())) {
tickPot(state, world, location); tickPot(state, world, location, offlineTick);
} }
} }
@@ -211,9 +211,8 @@ public class PotBlock extends AbstractCustomCropsBlock {
} }
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
"Overwrite old data with " + state + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );
}); });
ActionManager.trigger(context, config.placeActions()); ActionManager.trigger(context, config.placeActions());
@@ -324,15 +323,14 @@ public class PotBlock extends AbstractCustomCropsBlock {
id(state, potConfig.id()); id(state, potConfig.id());
water(state, potConfig.isWet(blockID) ? 1 : 0); water(state, potConfig.isWet(blockID) ? 1 : 0);
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
"Overwrite old data with " + state + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );
}); });
return state; 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); PotConfig config = config(state);
if (config == null) { 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."); 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 @@ public class PotBlock extends AbstractCustomCropsBlock {
} }
ActionManager.trigger(Context.block(state) 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) { public int water(CustomCropsBlockState state) {

View File

@@ -40,12 +40,13 @@ public class ScarecrowBlock extends AbstractCustomCropsBlock {
} }
@Override @Override
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) { public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
//tickScarecrow(world, location); //tickScarecrow(world, location);
} }
@Override @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); tickScarecrow(world, location);
} }
@@ -89,9 +90,8 @@ public class ScarecrowBlock extends AbstractCustomCropsBlock {
Pos3 pos3 = Pos3.from(event.location()); Pos3 pos3 = Pos3.from(event.location());
CustomCropsWorld<?> world = event.world(); CustomCropsWorld<?> world = event.world();
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
"Overwrite old data with " + state + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );
}); });
} }
@@ -108,7 +108,7 @@ public class ScarecrowBlock extends AbstractCustomCropsBlock {
} }
CustomCropsBlockState state = createBlockState(); CustomCropsBlockState state = createBlockState();
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() ->
"Overwrite old data with " + state + "Overwrite old data with " + state +
" at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous " at pos3[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );

View File

@@ -57,16 +57,16 @@ public class SprinklerBlock extends AbstractCustomCropsBlock {
} }
@Override @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())) { if (!world.setting().randomTickSprinkler() && canTick(state, world.setting().tickSprinklerInterval())) {
tickSprinkler(state, world, location); tickSprinkler(state, world, location, offlineTick);
} }
} }
@Override @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())) { if (world.setting().randomTickSprinkler() && canTick(state, world.setting().tickSprinklerInterval())) {
tickSprinkler(state, world, location); tickSprinkler(state, world, location, offlineTick);
} }
} }
@@ -231,15 +231,14 @@ public class SprinklerBlock extends AbstractCustomCropsBlock {
id(state, sprinklerConfig.id()); id(state, sprinklerConfig.id());
water(state, blockID.equals(sprinklerConfig.threeDItemWithWater()) ? 1 : 0); water(state, blockID.equals(sprinklerConfig.threeDItemWithWater()) ? 1 : 0);
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
"Overwrite old data with " + state + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );
}); });
return state; 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); SprinklerConfig config = config(state);
if (config == null) { 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."); 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 @@ public class SprinklerBlock extends AbstractCustomCropsBlock {
updateState = false; updateState = false;
} }
Context<CustomCropsBlockState> context = Context.block(state); Context<CustomCropsBlockState> context = Context.block(state).arg(ContextKeys.OFFLINE, offline);
World bukkitWorld = world.bukkitWorld(); World bukkitWorld = world.bukkitWorld();
Location bukkitLocation = location.toLocation(bukkitWorld); Location bukkitLocation = location.toLocation(bukkitWorld);
context.arg(ContextKeys.LOCATION, bukkitLocation); context.arg(ContextKeys.LOCATION, bukkitLocation);

View File

@@ -26,11 +26,11 @@ import net.momirealms.customcrops.api.core.BuiltInItemMechanics;
import net.momirealms.customcrops.api.core.InteractionResult; import net.momirealms.customcrops.api.core.InteractionResult;
import net.momirealms.customcrops.api.core.Registries; import net.momirealms.customcrops.api.core.Registries;
import net.momirealms.customcrops.api.core.block.CropBlock; 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.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.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; 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.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3; import net.momirealms.customcrops.api.core.world.Pos3;

View File

@@ -117,9 +117,8 @@ public class SeedItem extends AbstractCustomCropsItem {
BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(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); cropBlock.point(state, point);
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
"Overwrite old data with " + state + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );
}); });

View File

@@ -110,9 +110,8 @@ public class SprinklerItem extends AbstractCustomCropsItem {
// place the sprinkler // place the sprinkler
BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(targetLocation), config.existenceForm(), config.threeDItem(), FurnitureRotation.NONE); BukkitCustomCropsPlugin.getInstance().getItemManager().place(LocationUtils.toSurfaceCenterLocation(targetLocation), config.existenceForm(), config.threeDItem(), FurnitureRotation.NONE);
world.addBlockState(pos3, state).ifPresent(previous -> { world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug( BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
"Overwrite old data with " + state + " at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
); );
}); });
ActionManager.trigger(context, config.placeActions()); ActionManager.trigger(context, config.placeActions());

View File

@@ -24,9 +24,10 @@ import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.context.Context;
import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.*; import net.momirealms.customcrops.api.core.*;
import net.momirealms.customcrops.api.core.block.*; import net.momirealms.customcrops.api.core.block.PotBlock;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; import net.momirealms.customcrops.api.core.block.SprinklerBlock;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; 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.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig; import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;

View File

@@ -107,14 +107,14 @@ public interface CustomCropsChunk {
* *
* @return The unloaded time in seconds. * @return The unloaded time in seconds.
*/ */
int unloadedSeconds(); int lazySeconds();
/** /**
* Sets the time in seconds since the chunk was unloaded. * 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. * Gets the last time the chunk was loaded.
@@ -126,7 +126,7 @@ public interface CustomCropsChunk {
/** /**
* Updates the last loaded time to the current time. * Updates the last loaded time to the current time.
*/ */
void updateLastLoadedTime(); void updateLastUnloadTime();
/** /**
* Gets the time in milliseconds since the chunk was loaded. * Gets the time in milliseconds since the chunk was loaded.
@@ -203,11 +203,6 @@ public interface CustomCropsChunk {
*/ */
Optional<CustomCropsSection> removeSection(int sectionID); Optional<CustomCropsSection> removeSection(int sectionID);
/**
* Resets the unloaded time counter to zero.
*/
void resetUnloadedSeconds();
/** /**
* Checks if the chunk can be pruned (removed from memory or storage). * Checks if the chunk can be pruned (removed from memory or storage).
* *
@@ -222,6 +217,11 @@ public interface CustomCropsChunk {
*/ */
boolean isOfflineTaskNotified(); boolean isOfflineTaskNotified();
/**
* notify offline tasks
*/
void notifyOfflineTask();
/** /**
* Gets the queue of delayed tick tasks for this chunk. * Gets the queue of delayed tick tasks for this chunk.
* *

View File

@@ -32,30 +32,31 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
private final ConcurrentHashMap<Integer, CustomCropsSection> loadedSections; private final ConcurrentHashMap<Integer, CustomCropsSection> loadedSections;
private final PriorityQueue<DelayedTickTask> queue; private final PriorityQueue<DelayedTickTask> queue;
private final Set<BlockPos> tickedBlocks; private final Set<BlockPos> tickedBlocks;
private long lastLoadedTime; private long lastUnloadTime;
private int loadedSeconds; private int loadedSeconds;
private int unloadedSeconds; private int lazySeconds;
private boolean notified; private boolean notified;
private boolean isLoaded; private boolean isLoaded;
private boolean forceLoad; private boolean forceLoad;
// new chunk
protected CustomCropsChunkImpl(CustomCropsWorld<?> world, ChunkPos chunkPos) { protected CustomCropsChunkImpl(CustomCropsWorld<?> world, ChunkPos chunkPos) {
this.world = world; this.world = world;
this.chunkPos = chunkPos; this.chunkPos = chunkPos;
this.loadedSections = new ConcurrentHashMap<>(16); this.loadedSections = new ConcurrentHashMap<>(16);
this.queue = new PriorityQueue<>(); this.queue = new PriorityQueue<>();
this.unloadedSeconds = 0; this.lazySeconds = 0;
this.tickedBlocks = Collections.synchronizedSet(new HashSet<>()); this.tickedBlocks = Collections.synchronizedSet(new HashSet<>());
this.updateLastLoadedTime();
this.notified = true; this.notified = true;
this.isLoaded = false; this.isLoaded = false;
this.updateLastUnloadTime();
} }
protected CustomCropsChunkImpl( protected CustomCropsChunkImpl(
CustomCropsWorld<?> world, CustomCropsWorld<?> world,
ChunkPos chunkPos, ChunkPos chunkPos,
int loadedSeconds, int loadedSeconds,
long lastLoadedTime, long lastUnloadTime,
ConcurrentHashMap<Integer, CustomCropsSection> loadedSections, ConcurrentHashMap<Integer, CustomCropsSection> loadedSections,
PriorityQueue<DelayedTickTask> queue, PriorityQueue<DelayedTickTask> queue,
HashSet<BlockPos> tickedBlocks HashSet<BlockPos> tickedBlocks
@@ -63,11 +64,13 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
this.world = world; this.world = world;
this.chunkPos = chunkPos; this.chunkPos = chunkPos;
this.loadedSections = loadedSections; this.loadedSections = loadedSections;
this.lastLoadedTime = lastLoadedTime; this.lastUnloadTime = lastUnloadTime;
this.loadedSeconds = loadedSeconds; this.loadedSeconds = loadedSeconds;
this.queue = queue; this.queue = queue;
this.unloadedSeconds = 0; this.lazySeconds = 0;
this.tickedBlocks = Collections.synchronizedSet(tickedBlocks); this.tickedBlocks = Collections.synchronizedSet(tickedBlocks);
this.notified = false;
this.isLoaded = false;
} }
@Override @Override
@@ -85,6 +88,7 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
if (!isLoaded()) { if (!isLoaded()) {
if (((CustomCropsWorldImpl<?>) world).loadChunk(this)) { if (((CustomCropsWorldImpl<?>) world).loadChunk(this)) {
this.isLoaded = true; this.isLoaded = true;
this.lazySeconds = 0;
} }
if (loadBukkitChunk && !this.world.bukkitWorld().isChunkLoaded(chunkPos.x(), chunkPos.z())) { if (loadBukkitChunk && !this.world.bukkitWorld().isChunkLoaded(chunkPos.x(), chunkPos.z())) {
this.world.bukkitWorld().getChunkAt(chunkPos.x(), chunkPos.z()); this.world.bukkitWorld().getChunkAt(chunkPos.x(), chunkPos.z());
@@ -97,6 +101,8 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
if (isLoaded() && !isForceLoaded()) { if (isLoaded() && !isForceLoaded()) {
if (((CustomCropsWorldImpl<?>) world).unloadChunk(this, lazy)) { if (((CustomCropsWorldImpl<?>) world).unloadChunk(this, lazy)) {
this.isLoaded = false; this.isLoaded = false;
this.notified = false;
this.lazySeconds = 0;
} }
} }
} }
@@ -140,8 +146,8 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
this.queue.clear(); this.queue.clear();
this.arrangeTasks(interval); this.arrangeTasks(interval);
} }
scheduledTick(); scheduledTick(false);
randomTick(setting.randomTickSpeed()); randomTick(setting.randomTickSpeed(), false);
} }
private void arrangeTasks(int unit) { private void arrangeTasks(int unit) {
@@ -157,7 +163,7 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
} }
} }
private void scheduledTick() { private void scheduledTick(boolean offline) {
while (!queue.isEmpty() && queue.peek().getTime() <= loadedSeconds) { while (!queue.isEmpty() && queue.peek().getTime() <= loadedSeconds) {
DelayedTickTask task = queue.poll(); DelayedTickTask task = queue.poll();
if (task != null) { if (task != null) {
@@ -165,13 +171,13 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
CustomCropsSection section = loadedSections.get(pos.sectionID()); CustomCropsSection section = loadedSections.get(pos.sectionID());
if (section != null) { if (section != null) {
Optional<CustomCropsBlockState> block = section.getBlockState(pos); Optional<CustomCropsBlockState> 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(); ThreadLocalRandom random = ThreadLocalRandom.current();
for (CustomCropsSection section : loadedSections.values()) { for (CustomCropsSection section : loadedSections.values()) {
int sectionID = section.getSectionID(); int sectionID = section.getSectionID();
@@ -182,34 +188,34 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
int z = random.nextInt(16); int z = random.nextInt(16);
BlockPos pos = new BlockPos(x,y,z); BlockPos pos = new BlockPos(x,y,z);
Optional<CustomCropsBlockState> block = section.getBlockState(pos); Optional<CustomCropsBlockState> 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 @Override
public int unloadedSeconds() { public int lazySeconds() {
return unloadedSeconds; return lazySeconds;
} }
@Override @Override
public void unloadedSeconds(int unloadedSeconds) { public void lazySeconds(int lazySeconds) {
this.unloadedSeconds = unloadedSeconds; this.lazySeconds = lazySeconds;
} }
@Override @Override
public long lastLoadedTime() { public long lastLoadedTime() {
return lastLoadedTime; return lastUnloadTime;
} }
@Override @Override
public void updateLastLoadedTime() { public void updateLastUnloadTime() {
this.lastLoadedTime = System.currentTimeMillis(); this.lastUnloadTime = System.currentTimeMillis();
} }
@Override @Override
public int loadedMilliSeconds() { public int loadedMilliSeconds() {
return (int) (System.currentTimeMillis() - lastLoadedTime); return (int) (System.currentTimeMillis() - lastUnloadTime);
} }
@NotNull @NotNull
@@ -266,12 +272,6 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
return Optional.ofNullable(loadedSections.remove(sectionID)); return Optional.ofNullable(loadedSections.remove(sectionID));
} }
@Override
public void resetUnloadedSeconds() {
this.unloadedSeconds = 0;
this.notified = false;
}
@Override @Override
public boolean canPrune() { public boolean canPrune() {
return loadedSections.isEmpty(); return loadedSections.isEmpty();
@@ -282,6 +282,36 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
return notified; 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 @Override
public PriorityQueue<DelayedTickTask> tickTaskQueue() { public PriorityQueue<DelayedTickTask> tickTaskQueue() {
return queue; return queue;

View File

@@ -272,11 +272,11 @@ public class CustomCropsWorldImpl<W> implements CustomCropsWorld<W> {
ArrayList<CustomCropsChunk> chunksToSave = new ArrayList<>(); ArrayList<CustomCropsChunk> chunksToSave = new ArrayList<>();
for (Map.Entry<ChunkPos, CustomCropsChunk> lazyEntry : this.lazyChunks.entrySet()) { for (Map.Entry<ChunkPos, CustomCropsChunk> lazyEntry : this.lazyChunks.entrySet()) {
CustomCropsChunk chunk = lazyEntry.getValue(); CustomCropsChunk chunk = lazyEntry.getValue();
int sec = chunk.unloadedSeconds() + 1; int sec = chunk.lazySeconds() + 1;
if (sec >= 30) { if (sec >= 30) {
chunksToSave.add(chunk); chunksToSave.add(chunk);
} else { } else {
chunk.unloadedSeconds(sec); chunk.lazySeconds(sec);
} }
} }
for (CustomCropsChunk chunk : chunksToSave) { for (CustomCropsChunk chunk : chunksToSave) {
@@ -309,9 +309,6 @@ public class CustomCropsWorldImpl<W> implements CustomCropsWorld<W> {
this.setting = setting; this.setting = setting;
} }
/*
* Chunks
*/
@Nullable @Nullable
public CustomCropsChunk removeLazyChunk(ChunkPos chunkPos) { public CustomCropsChunk removeLazyChunk(ChunkPos chunkPos) {
return this.lazyChunks.remove(chunkPos); return this.lazyChunks.remove(chunkPos);
@@ -355,7 +352,7 @@ public class CustomCropsWorldImpl<W> implements CustomCropsWorld<W> {
return false; return false;
} }
this.loadedChunks.remove(chunk.chunkPos()); this.loadedChunks.remove(chunk.chunkPos());
chunk.updateLastLoadedTime(); chunk.updateLastUnloadTime();
if (lazy) { if (lazy) {
this.lazyChunks.put(pos, chunk); this.lazyChunks.put(pos, chunk);
} else { } else {
@@ -368,7 +365,7 @@ public class CustomCropsWorldImpl<W> implements CustomCropsWorld<W> {
public boolean unloadChunk(ChunkPos pos, boolean lazy) { public boolean unloadChunk(ChunkPos pos, boolean lazy) {
CustomCropsChunk removed = this.loadedChunks.remove(pos); CustomCropsChunk removed = this.loadedChunks.remove(pos);
if (removed != null) { if (removed != null) {
removed.updateLastLoadedTime(); removed.updateLastUnloadTime();
if (lazy) { if (lazy) {
this.lazyChunks.put(pos, removed); this.lazyChunks.put(pos, removed);
} else { } else {

View File

@@ -23,7 +23,6 @@ import net.momirealms.customcrops.common.plugin.feature.Reloadable;
import org.bukkit.World; import org.bukkit.World;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
/** /**

View File

@@ -37,6 +37,7 @@ public class WorldSetting implements Cloneable {
private final int sprinklerPerChunk; private final int sprinklerPerChunk;
private final int randomTickSpeed; private final int randomTickSpeed;
private final int maxOfflineTime; private final int maxOfflineTime;
private final int maxLoadingTime;
private final boolean tickCropRandomly; private final boolean tickCropRandomly;
private final boolean tickPotRandomly; private final boolean tickPotRandomly;
private final boolean tickSprinklerRandomly; private final boolean tickSprinklerRandomly;
@@ -54,6 +55,7 @@ public class WorldSetting implements Cloneable {
* @param tickSprinklerInterval The interval for ticking sprinklers. * @param tickSprinklerInterval The interval for ticking sprinklers.
* @param offlineTick Whether offline ticking is enabled. * @param offlineTick Whether offline ticking is enabled.
* @param maxOfflineTime The maximum offline time allowed. * @param maxOfflineTime The maximum offline time allowed.
* @param maxLoadingTime The maximum time allowed to load.
* @param enableSeason Whether seasons are enabled. * @param enableSeason Whether seasons are enabled.
* @param autoSeasonChange Whether season change is automatic. * @param autoSeasonChange Whether season change is automatic.
* @param seasonDuration The duration of each season. * @param seasonDuration The duration of each season.
@@ -73,6 +75,7 @@ public class WorldSetting implements Cloneable {
int tickSprinklerInterval, int tickSprinklerInterval,
boolean offlineTick, boolean offlineTick,
int maxOfflineTime, int maxOfflineTime,
int maxLoadingTime,
boolean enableSeason, boolean enableSeason,
boolean autoSeasonChange, boolean autoSeasonChange,
int seasonDuration, int seasonDuration,
@@ -88,6 +91,7 @@ public class WorldSetting implements Cloneable {
this.tickSprinklerInterval = tickSprinklerInterval; this.tickSprinklerInterval = tickSprinklerInterval;
this.offlineTick = offlineTick; this.offlineTick = offlineTick;
this.maxOfflineTime = maxOfflineTime; this.maxOfflineTime = maxOfflineTime;
this.maxLoadingTime = maxLoadingTime;
this.enableSeason = enableSeason; this.enableSeason = enableSeason;
this.autoSeasonChange = autoSeasonChange; this.autoSeasonChange = autoSeasonChange;
this.seasonDuration = seasonDuration; this.seasonDuration = seasonDuration;
@@ -133,6 +137,7 @@ public class WorldSetting implements Cloneable {
int tickSprinklerInterval, int tickSprinklerInterval,
boolean offlineGrow, boolean offlineGrow,
int maxOfflineTime, int maxOfflineTime,
int maxLoadingTime,
boolean enableSeason, boolean enableSeason,
boolean autoSeasonChange, boolean autoSeasonChange,
int seasonDuration, int seasonDuration,
@@ -152,6 +157,7 @@ public class WorldSetting implements Cloneable {
tickSprinklerInterval, tickSprinklerInterval,
offlineGrow, offlineGrow,
maxOfflineTime, maxOfflineTime,
maxLoadingTime,
enableSeason, enableSeason,
autoSeasonChange, autoSeasonChange,
seasonDuration, seasonDuration,
@@ -216,6 +222,15 @@ public class WorldSetting implements Cloneable {
return 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. * Checks if seasons are enabled.
* *

View File

@@ -17,8 +17,8 @@
package net.momirealms.customcrops.api.event; 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.fertilizer.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;

View File

@@ -23,10 +23,10 @@ import net.momirealms.customcrops.api.action.Action;
import net.momirealms.customcrops.api.action.ActionManager; import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.context.ContextKeys; import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.ConfigManager; 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.GreenhouseBlock;
import net.momirealms.customcrops.api.core.block.PotBlock; import net.momirealms.customcrops.api.core.block.PotBlock;
import net.momirealms.customcrops.api.core.block.ScarecrowBlock; 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.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;

View File

@@ -32,7 +32,10 @@ import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method; 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.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;

View File

@@ -15,7 +15,7 @@ asm_commons_version=9.7
jar_relocator_version=1.7 jar_relocator_version=1.7
adventure_bundle_version=4.17.0 adventure_bundle_version=4.17.0
adventure_platform_version=4.3.4 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_core_version=2.0.0-rc.2
cloud_services_version=2.0.0-rc.2 cloud_services_version=2.0.0-rc.2
cloud_brigadier_version=2.0.0-beta.9 cloud_brigadier_version=2.0.0-beta.9

View File

@@ -21,7 +21,10 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.ConfigManager; import net.momirealms.customcrops.api.core.ConfigManager;
import net.momirealms.customcrops.api.core.SimpleRegistryAccess; import net.momirealms.customcrops.api.core.SimpleRegistryAccess;
import net.momirealms.customcrops.api.core.block.*; 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.mechanic.fertilizer.FertilizerType;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; import net.momirealms.customcrops.api.event.CustomCropsReloadEvent;

View File

@@ -472,8 +472,8 @@ public class PlayerActionManager extends AbstractActionManager<Player> {
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld());
optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> { optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> {
CustomCropsBlock customCropsBlock = state.type(); CustomCropsBlock customCropsBlock = state.type();
customCropsBlock.randomTick(state, world, pos3); customCropsBlock.randomTick(state, world, pos3, false);
customCropsBlock.scheduledTick(state, world, pos3); customCropsBlock.scheduledTick(state, world, pos3, false);
if (customCropsBlock instanceof SprinklerBlock sprinklerBlock) { if (customCropsBlock instanceof SprinklerBlock sprinklerBlock) {
int water = sprinklerBlock.water(state); int water = sprinklerBlock.water(state);
SprinklerConfig config = sprinklerBlock.config(state); SprinklerConfig config = sprinklerBlock.config(state);
@@ -490,8 +490,8 @@ public class PlayerActionManager extends AbstractActionManager<Player> {
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld());
optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> { optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> {
CustomCropsBlock customCropsBlock = state.type(); CustomCropsBlock customCropsBlock = state.type();
customCropsBlock.randomTick(state, world, pos3); customCropsBlock.randomTick(state, world, pos3, false);
customCropsBlock.scheduledTick(state, world, pos3); customCropsBlock.scheduledTick(state, world, pos3, false);
})); }));
}, "tick"); }, "tick");
} }

View File

@@ -109,11 +109,11 @@ public class ForceTickCommand extends BukkitCommandFeature<CommandSender> {
private enum Mode { private enum Mode {
RANDOM_TICK(CustomCropsBlock::randomTick), RANDOM_TICK((customCropsBlock, state, world, location) -> customCropsBlock.randomTick(state, world, location, false)),
SCHEDULED_TICK(CustomCropsBlock::scheduledTick), SCHEDULED_TICK((customCropsBlock, state, world, location) -> customCropsBlock.scheduledTick(state, world, location, false)),
ALL((b, s, w, p) -> { ALL((b, s, w, p) -> {
b.randomTick(s, w, p); b.randomTick(s, w, p, false);
b.scheduledTick(s, w, p); b.scheduledTick(s, w, p, false);
}); });
private final QuadConsumer<CustomCropsBlock, CustomCropsBlockState, CustomCropsWorld<?>, Pos3> consumer; private final QuadConsumer<CustomCropsBlock, CustomCropsBlockState, CustomCropsWorld<?>, Pos3> consumer;

View File

@@ -30,13 +30,13 @@ import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import dev.dejvokep.boostedyaml.utils.format.NodeRole; import dev.dejvokep.boostedyaml.utils.format.NodeRole;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.*; 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.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; 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.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.common.helper.AdventureHelper; import net.momirealms.customcrops.common.helper.AdventureHelper;
import net.momirealms.customcrops.common.locale.TranslationManager; import net.momirealms.customcrops.common.locale.TranslationManager;
import net.momirealms.customcrops.common.plugin.CustomCropsProperties; import net.momirealms.customcrops.common.plugin.CustomCropsProperties;

View File

@@ -27,10 +27,10 @@ import net.momirealms.customcrops.api.core.ExistenceForm;
import net.momirealms.customcrops.api.core.Registries; import net.momirealms.customcrops.api.core.Registries;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; 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.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.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType; 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.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.misc.value.TextValue; import net.momirealms.customcrops.api.misc.value.TextValue;

View File

@@ -512,7 +512,7 @@ public class BukkitItemManager extends AbstractItemManager {
if (anyFurnitureID != null) { if (anyFurnitureID != null) {
if (!customCropsBlockState.type().isBlockInstance(anyFurnitureID)) { if (!customCropsBlockState.type().isBlockInstance(anyFurnitureID)) {
world.removeBlockState(pos3); 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 { } else {
event.setCancelled(true); event.setCancelled(true);
return; return;
@@ -521,7 +521,7 @@ public class BukkitItemManager extends AbstractItemManager {
String anyBlockID = blockID(location); String anyBlockID = blockID(location);
if (!customCropsBlockState.type().isBlockInstance(anyBlockID)) { if (!customCropsBlockState.type().isBlockInstance(anyBlockID)) {
world.removeBlockState(pos3); 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 { } else {
event.setCancelled(true); event.setCancelled(true);
return; return;

View File

@@ -194,7 +194,9 @@ public class BukkitWorldManager implements WorldManager, Listener {
adaptedWorld.setTicking(true); adaptedWorld.setTicking(true);
this.worlds.put(world.getName(), adaptedWorld); this.worlds.put(world.getName(), adaptedWorld);
for (Chunk chunk : world.getLoadedChunks()) { for (Chunk chunk : world.getLoadedChunks()) {
loadLoadedChunk(adaptedWorld, ChunkPos.fromBukkitChunk(chunk)); ChunkPos pos = ChunkPos.fromBukkitChunk(chunk);
loadLoadedChunk(adaptedWorld, pos);
notifyOfflineUpdates(adaptedWorld, pos);
} }
return adaptedWorld; return adaptedWorld;
} }
@@ -204,7 +206,13 @@ public class BukkitWorldManager implements WorldManager, Listener {
if (world.isChunkLoaded(pos)) return; if (world.isChunkLoaded(pos)) return;
Optional<CustomCropsChunk> customChunk = world.getChunk(pos); Optional<CustomCropsChunk> customChunk = world.getChunk(pos);
// don't load bukkit chunk again since it has been loaded // 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 @Override
@@ -251,12 +259,24 @@ public class BukkitWorldManager implements WorldManager, Listener {
public void onChunkLoad(ChunkLoadEvent event) { public void onChunkLoad(ChunkLoadEvent event) {
final Chunk chunk = event.getChunk(); final Chunk chunk = event.getChunk();
final World world = event.getWorld(); 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 @EventHandler
public void onEntitiesLoad(EntitiesLoadEvent event) { 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) { public boolean isMechanicEnabled(World world) {
@@ -329,6 +349,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
section.getInt("sprinkler.tick-interval", 2), section.getInt("sprinkler.tick-interval", 2),
section.getBoolean("offline-tick.enable", false), section.getBoolean("offline-tick.enable", false),
section.getInt("offline-tick.max-offline-seconds", 1200), section.getInt("offline-tick.max-offline-seconds", 1200),
section.getInt("offline-tick.max-loading-time", 100),
section.getBoolean("season.enable", false), section.getBoolean("season.enable", false),
section.getBoolean("season.auto-alternation", false), section.getBoolean("season.auto-alternation", false),
section.getInt("season.duration", 28), section.getInt("season.duration", 28),

View File

@@ -52,6 +52,11 @@ worlds:
# as it may cause chunks that have been unloaded for a long time # as it may cause chunks that have been unloaded for a long time
# taking long to load # taking long to load
max-offline-seconds: 1200 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 # Settings for crops
crop: crop:
# [RANDOM_TICK] # [RANDOM_TICK]