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.ContextKeys;
import net.momirealms.customcrops.api.core.*;
import net.momirealms.customcrops.api.core.block.*;
import net.momirealms.customcrops.api.core.block.BreakReason;
import net.momirealms.customcrops.api.core.block.CropBlock;
import net.momirealms.customcrops.api.core.block.PotBlock;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsChunk;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;

View File

@@ -21,11 +21,11 @@ import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.context.Context;
import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.block.*;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.block.CropBlock;
import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;

View File

@@ -12,15 +12,15 @@ import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import dev.dejvokep.boostedyaml.utils.format.NodeRole;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType;
import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.DeathCondition;
import net.momirealms.customcrops.api.core.mechanic.crop.GrowCondition;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.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.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.misc.water.FillMethod;
import net.momirealms.customcrops.api.misc.water.WateringMethod;

View File

@@ -17,13 +17,13 @@
package net.momirealms.customcrops.api.core;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.block.CustomCropsBlock;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.item.CustomCropsItem;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.common.annotation.DoNotUse;
import net.momirealms.customcrops.common.util.Key;

View File

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

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.ContextKeys;
import net.momirealms.customcrops.api.core.*;
import net.momirealms.customcrops.api.core.mechanic.crop.*;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.*;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
@@ -58,16 +58,16 @@ public class CropBlock extends AbstractCustomCropsBlock {
}
@Override
public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
if (!world.setting().randomTickCrop() && canTick(state, world.setting().tickCropInterval())) {
tickCrop(state, world, location);
tickCrop(state, world, location, offlineTick);
}
}
@Override
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
if (world.setting().randomTickCrop() && canTick(state, world.setting().tickCropInterval())) {
tickCrop(state, world, location);
tickCrop(state, world, location, offlineTick);
}
}
@@ -273,20 +273,20 @@ public class CropBlock extends AbstractCustomCropsBlock {
}
CropConfig cropConfig = configList.get(0);
CropStageConfig stageConfig = cropConfig.stageByID(stageID);
assert stageConfig != null;
int point = stageConfig.point();
CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState();
point(state, point);
id(state, cropConfig.id());
world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug(
"Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
);
});
return state;
}
private void tickCrop(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
private void tickCrop(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offline) {
CropConfig config = config(state);
if (config == null) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Crop data is removed at location[" + world.worldName() + "," + location + "] because the crop config[" + id(state) + "] has been removed.");
@@ -306,7 +306,7 @@ 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);
context.arg(ContextKeys.LOCATION, bukkitLocation);
for (DeathCondition deathCondition : config.deathConditions()) {

View File

@@ -34,9 +34,9 @@ public interface CustomCropsBlock {
CustomCropsBlockState createBlockState(CompoundMap data);
void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location);
void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick);
void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location);
void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick);
void onInteract(WrappedInteractEvent event);

View File

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

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.ContextKeys;
import net.momirealms.customcrops.api.core.*;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;
@@ -67,16 +67,16 @@ public class PotBlock extends AbstractCustomCropsBlock {
}
@Override
public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
if (!world.setting().randomTickPot() && canTick(state, world.setting().tickPotInterval())) {
tickPot(state, world, location);
tickPot(state, world, location, offlineTick);
}
}
@Override
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
if (world.setting().randomTickPot() && canTick(state, world.setting().tickPotInterval())) {
tickPot(state, world, location);
tickPot(state, world, location, offlineTick);
}
}
@@ -211,9 +211,8 @@ public class PotBlock extends AbstractCustomCropsBlock {
}
world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug(
"Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
);
});
ActionManager.trigger(context, config.placeActions());
@@ -324,15 +323,14 @@ public class PotBlock extends AbstractCustomCropsBlock {
id(state, potConfig.id());
water(state, potConfig.isWet(blockID) ? 1 : 0);
world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug(
"Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
);
});
return state;
}
private void tickPot(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
private void tickPot(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offline) {
PotConfig config = config(state);
if (config == null) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Pot data is removed at location[" + world.worldName() + "," + location + "] because the pot config[" + id(state) + "] has been removed.");
@@ -411,7 +409,10 @@ public class PotBlock extends AbstractCustomCropsBlock {
}
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) {

View File

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

View File

@@ -57,16 +57,16 @@ public class SprinklerBlock extends AbstractCustomCropsBlock {
}
@Override
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
if (!world.setting().randomTickSprinkler() && canTick(state, world.setting().tickSprinklerInterval())) {
tickSprinkler(state, world, location);
tickSprinkler(state, world, location, offlineTick);
}
}
@Override
public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
public void scheduledTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
if (world.setting().randomTickSprinkler() && canTick(state, world.setting().tickSprinklerInterval())) {
tickSprinkler(state, world, location);
tickSprinkler(state, world, location, offlineTick);
}
}
@@ -231,15 +231,14 @@ public class SprinklerBlock extends AbstractCustomCropsBlock {
id(state, sprinklerConfig.id());
water(state, blockID.equals(sprinklerConfig.threeDItemWithWater()) ? 1 : 0);
world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug(
"Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
);
});
return state;
}
private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location) {
private void tickSprinkler(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offline) {
SprinklerConfig config = config(state);
if (config == null) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Sprinkler data is removed at location[" + world.worldName() + "," + location + "] because the sprinkler config[" + id(state) + "] has been removed.");
@@ -257,7 +256,7 @@ public class SprinklerBlock extends AbstractCustomCropsBlock {
updateState = false;
}
Context<CustomCropsBlockState> context = Context.block(state);
Context<CustomCropsBlockState> context = Context.block(state).arg(ContextKeys.OFFLINE, offline);
World bukkitWorld = world.bukkitWorld();
Location bukkitLocation = location.toLocation(bukkitWorld);
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.Registries;
import net.momirealms.customcrops.api.core.block.CropBlock;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.block.PotBlock;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;

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);
cropBlock.point(state, point);
world.addBlockState(pos3, state).ifPresent(previous -> {
BukkitCustomCropsPlugin.getInstance().debug(
"Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
BukkitCustomCropsPlugin.getInstance().debug(() -> "Overwrite old data with " + state +
" at location[" + world.worldName() + "," + pos3 + "] which used to be " + previous
);
});

View File

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

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.ContextKeys;
import net.momirealms.customcrops.api.core.*;
import net.momirealms.customcrops.api.core.block.*;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.block.PotBlock;
import net.momirealms.customcrops.api.core.block.SprinklerBlock;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;

View File

@@ -107,14 +107,14 @@ public interface CustomCropsChunk {
*
* @return The unloaded time in seconds.
*/
int unloadedSeconds();
int lazySeconds();
/**
* Sets the time in seconds since the chunk was unloaded.
*
* @param unloadedSeconds The unloaded time to set.
* @param lazySeconds The unloaded time to set.
*/
void unloadedSeconds(int unloadedSeconds);
void lazySeconds(int lazySeconds);
/**
* Gets the last time the chunk was loaded.
@@ -126,7 +126,7 @@ public interface CustomCropsChunk {
/**
* Updates the last loaded time to the current time.
*/
void updateLastLoadedTime();
void updateLastUnloadTime();
/**
* Gets the time in milliseconds since the chunk was loaded.
@@ -203,11 +203,6 @@ public interface CustomCropsChunk {
*/
Optional<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).
*
@@ -222,6 +217,11 @@ public interface CustomCropsChunk {
*/
boolean isOfflineTaskNotified();
/**
* notify offline tasks
*/
void notifyOfflineTask();
/**
* 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 PriorityQueue<DelayedTickTask> queue;
private final Set<BlockPos> tickedBlocks;
private long lastLoadedTime;
private long lastUnloadTime;
private int loadedSeconds;
private int unloadedSeconds;
private int lazySeconds;
private boolean notified;
private boolean isLoaded;
private boolean forceLoad;
// new chunk
protected CustomCropsChunkImpl(CustomCropsWorld<?> world, ChunkPos chunkPos) {
this.world = world;
this.chunkPos = chunkPos;
this.loadedSections = new ConcurrentHashMap<>(16);
this.queue = new PriorityQueue<>();
this.unloadedSeconds = 0;
this.lazySeconds = 0;
this.tickedBlocks = Collections.synchronizedSet(new HashSet<>());
this.updateLastLoadedTime();
this.notified = true;
this.isLoaded = false;
this.updateLastUnloadTime();
}
protected CustomCropsChunkImpl(
CustomCropsWorld<?> world,
ChunkPos chunkPos,
int loadedSeconds,
long lastLoadedTime,
long lastUnloadTime,
ConcurrentHashMap<Integer, CustomCropsSection> loadedSections,
PriorityQueue<DelayedTickTask> queue,
HashSet<BlockPos> tickedBlocks
@@ -63,11 +64,13 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
this.world = world;
this.chunkPos = chunkPos;
this.loadedSections = loadedSections;
this.lastLoadedTime = lastLoadedTime;
this.lastUnloadTime = lastUnloadTime;
this.loadedSeconds = loadedSeconds;
this.queue = queue;
this.unloadedSeconds = 0;
this.lazySeconds = 0;
this.tickedBlocks = Collections.synchronizedSet(tickedBlocks);
this.notified = false;
this.isLoaded = false;
}
@Override
@@ -85,6 +88,7 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
if (!isLoaded()) {
if (((CustomCropsWorldImpl<?>) world).loadChunk(this)) {
this.isLoaded = true;
this.lazySeconds = 0;
}
if (loadBukkitChunk && !this.world.bukkitWorld().isChunkLoaded(chunkPos.x(), chunkPos.z())) {
this.world.bukkitWorld().getChunkAt(chunkPos.x(), chunkPos.z());
@@ -97,6 +101,8 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
if (isLoaded() && !isForceLoaded()) {
if (((CustomCropsWorldImpl<?>) world).unloadChunk(this, lazy)) {
this.isLoaded = false;
this.notified = false;
this.lazySeconds = 0;
}
}
}
@@ -140,8 +146,8 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
this.queue.clear();
this.arrangeTasks(interval);
}
scheduledTick();
randomTick(setting.randomTickSpeed());
scheduledTick(false);
randomTick(setting.randomTickSpeed(), false);
}
private void arrangeTasks(int unit) {
@@ -157,7 +163,7 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
}
}
private void scheduledTick() {
private void scheduledTick(boolean offline) {
while (!queue.isEmpty() && queue.peek().getTime() <= loadedSeconds) {
DelayedTickTask task = queue.poll();
if (task != null) {
@@ -165,13 +171,13 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
CustomCropsSection section = loadedSections.get(pos.sectionID());
if (section != null) {
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();
for (CustomCropsSection section : loadedSections.values()) {
int sectionID = section.getSectionID();
@@ -182,34 +188,34 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
int z = random.nextInt(16);
BlockPos pos = new BlockPos(x,y,z);
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
public int unloadedSeconds() {
return unloadedSeconds;
public int lazySeconds() {
return lazySeconds;
}
@Override
public void unloadedSeconds(int unloadedSeconds) {
this.unloadedSeconds = unloadedSeconds;
public void lazySeconds(int lazySeconds) {
this.lazySeconds = lazySeconds;
}
@Override
public long lastLoadedTime() {
return lastLoadedTime;
return lastUnloadTime;
}
@Override
public void updateLastLoadedTime() {
this.lastLoadedTime = System.currentTimeMillis();
public void updateLastUnloadTime() {
this.lastUnloadTime = System.currentTimeMillis();
}
@Override
public int loadedMilliSeconds() {
return (int) (System.currentTimeMillis() - lastLoadedTime);
return (int) (System.currentTimeMillis() - lastUnloadTime);
}
@NotNull
@@ -266,12 +272,6 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
return Optional.ofNullable(loadedSections.remove(sectionID));
}
@Override
public void resetUnloadedSeconds() {
this.unloadedSeconds = 0;
this.notified = false;
}
@Override
public boolean canPrune() {
return loadedSections.isEmpty();
@@ -282,6 +282,36 @@ public class CustomCropsChunkImpl implements CustomCropsChunk {
return notified;
}
@Override
public void notifyOfflineTask() {
if (isOfflineTaskNotified()) return;
this.notified = true;
long current = System.currentTimeMillis();
int offlineTimeInSeconds = (int) (current - lastLoadedTime()) / 1000;
WorldSetting setting = world.setting();
offlineTimeInSeconds = Math.min(offlineTimeInSeconds, setting.maxOfflineTime());
int minTickUnit = setting.minTickUnit();
int randomTickSpeed = setting.randomTickSpeed();
int threshold = setting.maxLoadingTime();
int i = 0;
long time1 = System.currentTimeMillis();
while (i < offlineTimeInSeconds) {
this.loadedSeconds++;
if (this.loadedSeconds >= minTickUnit) {
this.loadedSeconds = 0;
this.tickedBlocks.clear();
this.queue.clear();
this.arrangeTasks(minTickUnit);
}
scheduledTick(true);
randomTick(randomTickSpeed, true);
i++;
if (System.currentTimeMillis() - time1 > threshold) {
break;
}
}
}
@Override
public PriorityQueue<DelayedTickTask> tickTaskQueue() {
return queue;

View File

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

View File

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

View File

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

View File

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

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.context.ContextKeys;
import net.momirealms.customcrops.api.core.ConfigManager;
import net.momirealms.customcrops.api.core.mechanic.crop.CrowAttack;
import net.momirealms.customcrops.api.core.block.GreenhouseBlock;
import net.momirealms.customcrops.api.core.block.PotBlock;
import net.momirealms.customcrops.api.core.block.ScarecrowBlock;
import net.momirealms.customcrops.api.core.mechanic.crop.CrowAttack;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;

View File

@@ -32,7 +32,10 @@ import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
import java.util.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.PriorityQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

View File

@@ -15,7 +15,7 @@ asm_commons_version=9.7
jar_relocator_version=1.7
adventure_bundle_version=4.17.0
adventure_platform_version=4.3.4
sparrow_heart_version=0.40
sparrow_heart_version=0.42
cloud_core_version=2.0.0-rc.2
cloud_services_version=2.0.0-rc.2
cloud_brigadier_version=2.0.0-beta.9

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.SimpleRegistryAccess;
import net.momirealms.customcrops.api.core.block.*;
import net.momirealms.customcrops.api.core.item.*;
import net.momirealms.customcrops.api.core.item.FertilizerItem;
import net.momirealms.customcrops.api.core.item.SeedItem;
import net.momirealms.customcrops.api.core.item.SprinklerItem;
import net.momirealms.customcrops.api.core.item.WateringCanItem;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.event.CustomCropsReloadEvent;

View File

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

View File

@@ -109,11 +109,11 @@ public class ForceTickCommand extends BukkitCommandFeature<CommandSender> {
private enum Mode {
RANDOM_TICK(CustomCropsBlock::randomTick),
SCHEDULED_TICK(CustomCropsBlock::scheduledTick),
RANDOM_TICK((customCropsBlock, state, world, location) -> customCropsBlock.randomTick(state, world, location, false)),
SCHEDULED_TICK((customCropsBlock, state, world, location) -> customCropsBlock.scheduledTick(state, world, location, false)),
ALL((b, s, w, p) -> {
b.randomTick(s, w, p);
b.scheduledTick(s, w, p);
b.randomTick(s, w, p, false);
b.scheduledTick(s, w, p, false);
});
private final QuadConsumer<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 net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.*;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.DeathCondition;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.DeathCondition;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.common.helper.AdventureHelper;
import net.momirealms.customcrops.common.locale.TranslationManager;
import net.momirealms.customcrops.common.plugin.CustomCropsProperties;

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.mechanic.crop.CropConfig;
import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig;
import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerType;
import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig;
import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig;
import net.momirealms.customcrops.api.core.mechanic.wateringcan.WateringCanConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.misc.value.TextValue;

View File

@@ -512,7 +512,7 @@ public class BukkitItemManager extends AbstractItemManager {
if (anyFurnitureID != null) {
if (!customCropsBlockState.type().isBlockInstance(anyFurnitureID)) {
world.removeBlockState(pos3);
plugin.debug("[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState);
plugin.debug(() -> "[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState);
} else {
event.setCancelled(true);
return;
@@ -521,7 +521,7 @@ public class BukkitItemManager extends AbstractItemManager {
String anyBlockID = blockID(location);
if (!customCropsBlockState.type().isBlockInstance(anyBlockID)) {
world.removeBlockState(pos3);
plugin.debug("[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState);
plugin.debug(() -> "[" + location.getWorld().getName() + "] Removed inconsistent block data at " + pos3 + " which used to be " + customCropsBlockState);
} else {
event.setCancelled(true);
return;

View File

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

View File

@@ -52,6 +52,11 @@ worlds:
# as it may cause chunks that have been unloaded for a long time
# taking long to load
max-offline-seconds: 1200
# The max time used in loading this chunk
# Sometimes a chunk might contain a lot of data and has not been loaded for a long time,
# thus taking a long time loading and causing unexpected issues
# This setting allows the plugin to forcefully interrupt the tick process if the time consumed has exceeded a certain value
max-loading-time: 100 #ms
# Settings for crops
crop:
# [RANDOM_TICK]