9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-28 19:39:11 +00:00

add crop block

This commit is contained in:
XiaoMoMi
2025-03-29 00:39:09 +08:00
parent 80570d6f42
commit cd30c5b3b3
22 changed files with 316 additions and 84 deletions

View File

@@ -34,46 +34,16 @@ public class CropBlockBehavior extends BushBlockBehavior {
this.minGrowLight = minGrowLight;
}
public final boolean isMaxAge(Object state) {
return this.getAge(state) >= this.ageProperty.max;
}
public final boolean isMaxAge(ImmutableBlockState state) {
return this.getAge(state) >= this.ageProperty.max;
}
public static ImmutableBlockState getCEBlockState(Object nmsState) {
return BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(nmsState));
}
public final int getAge(Object state) {
return getCEBlockState(state).get(ageProperty);
}
public final int getAge(ImmutableBlockState state) {
return state.get(ageProperty);
}
public Object getStateForAge(Object state, int age) {
ImmutableBlockState afterState = getCEBlockState(state).owner().value().defaultState().with(ageProperty, age);
return afterState.customBlockState().handle();
}
public void growCrops(Object level, Object pos, Object state) throws InvocationTargetException, IllegalAccessException {
int i = this.getAge(state) + RandomUtils.generateRandomInt(2, 5);
int maxAge = this.ageProperty.max;
if (i > maxAge) {
i = maxAge;
}
Reflections.method$Level$setBlock.invoke(level, pos, getStateForAge(state, i), UpdateOption.UPDATE_NONE.flags());
}
private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
return (int) Reflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0);
}
private boolean hasSufficientLight(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
return getRawBrightness(level, pos) >= minGrowLight - 1;
return getRawBrightness(level, pos) >= this.minGrowLight - 1;
}
@Override
@@ -81,11 +51,13 @@ public class CropBlockBehavior extends BushBlockBehavior {
Object state = args[0];
Object level = args[1];
Object pos = args[2];
if (getRawBrightness(level, pos) >= minGrowLight) {
int age = this.getAge(state);
float randomFloat = RandomUtils.generateRandomFloat(0, 1);
if (age < this.ageProperty.max && randomFloat < 1.0 / Math.floor(25.0 / this.growSpeed + 1.0)) {
Reflections.method$Level$setBlock.invoke(level, pos, getStateForAge(state, age + 1), UpdateOption.UPDATE_ALL.flags());
if (getRawBrightness(level, pos) >= this.minGrowLight) {
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (currentState != null && !currentState.isEmpty()) {
int age = this.getAge(currentState);
if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
Reflections.method$Level$setBlock.invoke(level, pos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
}
}
}
}
@@ -103,12 +75,30 @@ public class CropBlockBehavior extends BushBlockBehavior {
@Override
public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) {
Object state = args[2];
return !this.isMaxAge(state);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (immutableBlockState != null && !immutableBlockState.isEmpty()) {
return getAge(immutableBlockState) != this.ageProperty.max;
} else {
return false;
}
}
@Override
public void performBoneMeal(Object thisBlock, Object[] args) throws Exception {
this.growCrops(args[0], args[2], args[3]);
this.performBoneMeal(args[0], args[2], args[3]);
}
private void performBoneMeal(Object level, Object pos, Object state) throws InvocationTargetException, IllegalAccessException {
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (immutableBlockState == null || immutableBlockState.isEmpty()) {
return;
}
int i = this.getAge(immutableBlockState) + RandomUtils.generateRandomInt(2, 5);
int maxAge = this.ageProperty.max;
if (i > maxAge) {
i = maxAge;
}
Reflections.method$Level$setBlock.invoke(level, pos, immutableBlockState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
}
public static class Factory implements BlockBehaviorFactory {
@@ -122,8 +112,8 @@ public class CropBlockBehavior extends BushBlockBehavior {
throw new IllegalArgumentException("age property not set for crop");
}
// 存活条件是最小生长亮度-1
int minGrowLight = MiscUtils.getAsInt(arguments.getOrDefault("min-grow-light", 9));
float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1));
int minGrowLight = MiscUtils.getAsInt(arguments.getOrDefault("light-requirement", 9));
float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.25f));
return new CropBlockBehavior(tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight);
}
}

View File

@@ -5,11 +5,12 @@ import net.momirealms.craftengine.core.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.util.SectionPosUtils;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor;
public class BukkitCEWorld extends CEWorld {
public BukkitCEWorld(World world) {
super(world);
public BukkitCEWorld(World world, StorageAdaptor adaptor) {
super(world, adaptor);
}
@Override

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
@@ -8,6 +9,7 @@ import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.ChunkPos;
import net.momirealms.craftengine.core.world.SectionPos;
@@ -15,6 +17,8 @@ import net.momirealms.craftengine.core.world.WorldManager;
import net.momirealms.craftengine.core.world.chunk.CEChunk;
import net.momirealms.craftengine.core.world.chunk.CESection;
import net.momirealms.craftengine.core.world.chunk.serialization.ChunkSerializer;
import net.momirealms.craftengine.core.world.chunk.storage.DefaultStorageAdaptor;
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor;
import net.momirealms.sparrow.nbt.CompoundTag;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
@@ -27,6 +31,7 @@ import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.HashMap;
@@ -38,18 +43,34 @@ public class BukkitWorldManager implements WorldManager, Listener {
private static BukkitWorldManager instance;
private final BukkitCraftEngine plugin;
private final Map<UUID, CEWorld> worlds;
private CEWorld[] worldArray;
private CEWorld[] worldArray = new CEWorld[0];
private final ReentrantReadWriteLock worldMapLock = new ReentrantReadWriteLock();
private SchedulerTask tickTask;
// cache
private UUID lastVisitedUUID;
private CEWorld lastVisitedWorld;
private StorageAdaptor storageAdaptor;
public BukkitWorldManager(BukkitCraftEngine plugin) {
instance = this;
this.plugin = plugin;
this.worlds = new HashMap<>();
resetWorldArray();
if (VersionHelper.isVersionNewerThan1_21_4()) {
try {
Class.forName("com.infernalsuite.asp.api.AdvancedSlimePaperAPI");
SlimeFormatStorageAdaptor adaptor = new SlimeFormatStorageAdaptor(this);
this.storageAdaptor = adaptor;
Bukkit.getPluginManager().registerEvents(adaptor, plugin.bootstrap());
return;
} catch (ClassNotFoundException ignored) {
}
}
this.storageAdaptor = new DefaultStorageAdaptor();
}
@Override
public void setStorageAdaptor(@NotNull StorageAdaptor storageAdaptor) {
this.storageAdaptor = storageAdaptor;
}
public static BukkitWorldManager instance() {
@@ -95,7 +116,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
this.worldMapLock.writeLock().lock();
try {
for (World world : Bukkit.getWorlds()) {
CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world));
CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor);
this.worlds.put(world.getUID(), ceWorld);
this.resetWorldArray();
for (Chunk chunk : world.getLoadedChunks()) {
@@ -110,8 +131,11 @@ public class BukkitWorldManager implements WorldManager, Listener {
@Override
public void disable() {
HandlerList.unregisterAll(this);
if (tickTask != null && !tickTask.cancelled()) {
tickTask.cancel();
if (this.storageAdaptor instanceof Listener listener) {
HandlerList.unregisterAll(listener);
}
if (this.tickTask != null && !this.tickTask.cancelled()) {
this.tickTask.cancel();
}
for (World world : Bukkit.getWorlds()) {
@@ -125,14 +149,18 @@ public class BukkitWorldManager implements WorldManager, Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onWorldLoad(WorldLoadEvent event) {
World world = event.getWorld();
CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world));
this.loadWorld(new BukkitWorld(event.getWorld()));
}
@Override
public void loadWorld(net.momirealms.craftengine.core.world.World world) {
this.worldMapLock.writeLock().lock();
try {
if (this.worlds.containsKey(world.getUID())) return;
this.worlds.put(event.getWorld().getUID(), ceWorld);
if (this.worlds.containsKey(world.uuid())) return;
CEWorld ceWorld = new BukkitCEWorld(world, this.storageAdaptor);
this.worlds.put(world.uuid(), ceWorld);
this.resetWorldArray();
for (Chunk chunk : world.getLoadedChunks()) {
for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) {
handleChunkLoad(ceWorld, chunk);
}
} finally {
@@ -142,11 +170,15 @@ public class BukkitWorldManager implements WorldManager, Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onWorldUnload(WorldUnloadEvent event) {
World world = event.getWorld();
unloadWorld(new BukkitWorld(event.getWorld()));
}
@Override
public void unloadWorld(net.momirealms.craftengine.core.world.World world) {
CEWorld ceWorld;
this.worldMapLock.writeLock().lock();
try {
ceWorld = this.worlds.remove(world.getUID());
ceWorld = this.worlds.remove(world.uuid());
if (ceWorld == null) {
return;
}
@@ -158,11 +190,20 @@ public class BukkitWorldManager implements WorldManager, Listener {
} finally {
this.worldMapLock.writeLock().unlock();
}
for (Chunk chunk : world.getLoadedChunks()) {
for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) {
handleChunkUnload(ceWorld, chunk);
}
}
@Override
public <T> net.momirealms.craftengine.core.world.World wrap(T world) {
if (world instanceof World w) {
return new BukkitWorld(w);
} else {
throw new IllegalArgumentException(world.getClass() + " is not a Bukkit World");
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onChunkLoad(ChunkLoadEvent event) {
this.worldMapLock.readLock().lock();