9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-19 15:09:25 +00:00
This commit is contained in:
XiaoMoMi
2024-10-03 21:38:40 +08:00
parent b189020679
commit 99aa349565
11 changed files with 268 additions and 39 deletions

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.api;
import net.momirealms.customcrops.api.core.BuiltInBlockMechanics;
import net.momirealms.customcrops.api.core.ExistenceForm;
import net.momirealms.customcrops.api.core.FurnitureRotation;
import net.momirealms.customcrops.api.core.Registries;
import net.momirealms.customcrops.api.core.block.BreakReason;
import net.momirealms.customcrops.api.core.block.CropBlock;
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;
import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent;
import net.momirealms.customcrops.api.util.DummyCancellable;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import static java.util.Objects.requireNonNull;
public class BukkitCustomCropsAPI implements CustomCropsAPI {
private static CustomCropsAPI instance;
private final BukkitCustomCropsPlugin plugin;
public BukkitCustomCropsAPI(BukkitCustomCropsPlugin plugin) {
this.plugin = plugin;
instance = this;
}
public static CustomCropsAPI get() {
return instance;
}
@Override
public Pos3 adapt(Location location) {
return Pos3.from(location);
}
@Override
public @Nullable CustomCropsWorld<?> getCustomCropsWorld(String name) {
return plugin.getWorldManager().getWorld(name).orElse(null);
}
@Override
public @Nullable CustomCropsWorld<?> getCustomCropsWorld(World world) {
return plugin.getWorldManager().getWorld(world).orElse(null);
}
@Override
public boolean placeCrop(Location location, String id, int point) {
CropConfig cropConfig = Registries.CROP.get(id);
if (cropConfig == null) {
return false;
}
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld());
if (optionalWorld.isEmpty()) {
return false;
}
CustomCropsWorld<?> world = optionalWorld.get();
CropBlock cropBlock = (CropBlock) BuiltInBlockMechanics.CROP.mechanic();
CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState();
cropBlock.id(state, id);
cropBlock.point(state, point);
CropStageConfig stageConfigWithModel = cropConfig.stageWithModelByPoint(cropBlock.point(state));
world.addBlockState(Pos3.from(location), state);
plugin.getScheduler().sync().run(() -> {
plugin.getItemManager().remove(location, ExistenceForm.ANY);
plugin.getItemManager().place(location, stageConfigWithModel.existenceForm(), requireNonNull(stageConfigWithModel.stageID()), cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE);
}, location);
return true;
}
@Override
public void simulatePlayerBreakCrop(
Player player,
EquipmentSlot hand,
Location location,
BreakReason reason
) {
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
Pos3 pos3 = Pos3.from(location);
CustomCropsWorld<?> world = optionalWorld.get();
Optional<CustomCropsBlockState> optionalState = world.getBlockState(pos3);
if (optionalState.isEmpty()) {
return;
}
CustomCropsBlockState state = optionalState.get();
if (!(state.type() instanceof CropBlock cropBlock)) {
return;
}
CropConfig config = cropBlock.config(state);
if (config == null) {
return;
}
CropStageConfig stageConfig = config.stageWithModelByPoint(cropBlock.point(state));
DummyCancellable dummyCancellable = new DummyCancellable();
if (player != null) {
ItemStack itemStack = player.getInventory().getItem(hand);
state.type().onBreak(new WrappedBreakEvent(player, null, hand, location, stageConfig.stageID(), itemStack, plugin.getItemManager().id(itemStack), reason, world, dummyCancellable));
} else {
state.type().onBreak(new WrappedBreakEvent(null, null, null, location, stageConfig.stageID(), null, null, reason, world, dummyCancellable));
}
if (dummyCancellable.isCancelled()) {
return;
}
world.removeBlockState(pos3);
plugin.getItemManager().remove(location, ExistenceForm.ANY);
}
}

View File

@@ -60,6 +60,7 @@ public abstract class BukkitCustomCropsPlugin implements CustomCropsPlugin {
protected WorldManager worldManager;
protected RegistryAccess registryAccess;
protected SenderFactory<BukkitCustomCropsPlugin, CommandSender> senderFactory;
protected CustomCropsAPI api;
protected final Map<Class<?>, ActionManager<?>> actionManagers = new HashMap<>();
protected final Map<Class<?>, RequirementManager<?>> requirementManagers = new HashMap<>();
@@ -74,6 +75,7 @@ public abstract class BukkitCustomCropsPlugin implements CustomCropsPlugin {
throw new IllegalArgumentException("CustomCrops plugin requires custom crops plugin");
}
this.bootstrap = bootstrap;
this.api = new BukkitCustomCropsAPI(this);
instance = this;
}
@@ -200,6 +202,15 @@ public abstract class BukkitCustomCropsPlugin implements CustomCropsPlugin {
return registryAccess;
}
/**
* Get the API instance
*
* @return API
*/
public CustomCropsAPI getAPI() {
return api;
}
/**
* Retrieves an ActionManager for a specific type.
*

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.api;
import net.momirealms.customcrops.api.core.block.BreakReason;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.jetbrains.annotations.Nullable;
public interface CustomCropsAPI {
/**
* Adapts a Bukkit Location to a Pos3
*
* @param location location
* @return pos3
*/
Pos3 adapt(Location location);
/**
* Gets the CustomCrops world
*
* @param name world name
* @return the world
*/
@Nullable CustomCropsWorld<?> getCustomCropsWorld(String name);
/**
* Gets the CustomCrops world
*
* @param world Bukkit world
* @return the world
*/
@Nullable CustomCropsWorld<?> getCustomCropsWorld(World world);
/**
* Places a crop regardless of planting conditions such as pots.
*
* @param location location
* @param id crop id
* @param point point
* @return success or not
*/
boolean placeCrop(Location location, String id, int point);
/**
* Performs actions to destroy crops on behalf of the player.
*
* @param player player
* @param hand hand
* @param location location
* @param reason reason
*/
void simulatePlayerBreakCrop(Player player, EquipmentSlot hand, Location location, BreakReason reason);
}

View File

@@ -50,7 +50,6 @@ import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public class CropBlock extends AbstractCustomCropsBlock {
@@ -345,25 +344,7 @@ public class CropBlock extends AbstractCustomCropsBlock {
World bukkitWorld = world.bukkitWorld();
Location bukkitLocation = location.toLocation(bukkitWorld);
CompletableFuture<Boolean> future = new CompletableFuture<>();
if (ConfigManager.doubleCheck()) {
plugin.getScheduler().sync().run(() -> {
CropStageConfig nearest = config.stageWithModelByPoint(previousPoint);
String blockID = plugin.getItemManager().id(location.toLocation(bukkitWorld), nearest.existenceForm());
if (!config.stageIDs().contains(blockID)) {
plugin.getPluginLogger().warn("Crop[" + config.id() + "] is removed at location[" + world.worldName() + "," + location + "] because the id of the block is [" + blockID + "]");
world.removeBlockState(location);
future.complete(false);
return;
}
future.complete(true);
}, bukkitLocation);
} else {
future.complete(true);
}
future.thenAcceptAsync((run) -> {
if (!run) return;
Runnable task = () -> {
Context<CustomCropsBlockState> context = Context.block(state, bukkitLocation).arg(ContextKeys.OFFLINE, offline);
for (DeathCondition deathCondition : config.deathConditions()) {
if (deathCondition.isMet(context)) {
@@ -430,7 +411,22 @@ public class CropBlock extends AbstractCustomCropsBlock {
}
}
}, bukkitLocation);
}, world.scheduler().async());
};
if (ConfigManager.doubleCheck()) {
plugin.getScheduler().sync().run(() -> {
CropStageConfig nearest = config.stageWithModelByPoint(previousPoint);
String blockID = plugin.getItemManager().id(location.toLocation(bukkitWorld), nearest.existenceForm());
if (!config.stageIDs().contains(blockID)) {
plugin.getPluginLogger().warn("Crop[" + config.id() + "] is removed at location[" + world.worldName() + "," + location + "] because the id of the block is [" + blockID + "]");
world.removeBlockState(location);
return;
}
world.scheduler().async().execute(task);
}, bukkitLocation);
} else {
task.run();
}
}
public int point(CustomCropsBlockState state) {

View File

@@ -29,10 +29,7 @@ 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;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;
import net.momirealms.customcrops.api.core.world.Season;
import net.momirealms.customcrops.api.core.world.*;
import net.momirealms.customcrops.api.misc.value.MathValue;
import net.momirealms.customcrops.api.misc.value.TextValue;
import net.momirealms.customcrops.api.util.MoonPhase;
@@ -988,10 +985,15 @@ public abstract class AbstractRequirementManager<T> implements RequirementManage
for (int i = -range; i <= range; i++) {
for (int j = -range; j <= range; j++) {
for (int k : new int[]{0,-1,1}) {
Optional<CustomCropsBlockState> optionalState = customCropsWorld.getBlockState(pos3.add(i, k, j));
if (optionalState.isPresent() && optionalState.get().type() instanceof ScarecrowBlock) {
if (advanced) ActionManager.trigger(context, actions);
return false;
Pos3 tempPos3 = pos3.add(i, k, j);
Optional<CustomCropsChunk> optionalChunk = customCropsWorld.getLoadedChunk(tempPos3.toChunkPos());
if (optionalChunk.isPresent()) {
CustomCropsChunk chunk = optionalChunk.get();
Optional<CustomCropsBlockState> optionalState = chunk.getBlockState(pos3);
if (optionalState.isPresent() && optionalState.get().type() instanceof ScarecrowBlock) {
if (advanced) ActionManager.trigger(context, actions);
return false;
}
}
}
}

View File

@@ -41,7 +41,7 @@ public class MMOItemsItemProvider implements ItemProvider {
@NotNull
@Override
public ItemStack buildItem(@NotNull Player player, @NotNull String id) {
String[] split = id.split(":");
String[] split = id.split(":", 2);
MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ENGLISH));
return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build());
}
@@ -50,6 +50,10 @@ public class MMOItemsItemProvider implements ItemProvider {
public String itemID(@NotNull ItemStack itemStack) {
NBTItem nbtItem = NBTItem.get(itemStack);
if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null;
return nbtItem.getString("MMOITEMS_ITEM_ID");
if (nbtItem.hasType()) {
return nbtItem.getType() + ":" + nbtItem.getString("MMOITEMS_ITEM_ID");
} else {
return nbtItem.getString("MMOITEMS_ITEM_ID");
}
}
}

View File

@@ -1,6 +1,6 @@
# Project settings
# Rule: [major update].[feature update].[bug fix]
project_version=3.6.13
project_version=3.6.14
config_version=41
project_group=net.momirealms

View File

@@ -223,6 +223,7 @@ public class BukkitCustomCropsPluginImpl extends BukkitCustomCropsPlugin {
this.coolDownManager.reload();
this.translationManager.reload();
this.hologramManager.reload();
this.itemManager.reload();
this.actionManagers.values().forEach(Reloadable::reload);
this.requirementManagers.values().forEach(Reloadable::reload);

View File

@@ -196,7 +196,7 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor<World> {
if (parentDir != null && !parentDir.exists()) {
parentDir.mkdirs();
}
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
try (FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos)) {
bos.write(serializeRegion(region));
long time2 = System.currentTimeMillis();
BukkitCustomCropsPlugin.getInstance().debug(() -> "[" + world.worldName() + "] Took " + (time2-time1) + "ms to save region " + region.regionPos());

View File

@@ -328,7 +328,9 @@ public class BukkitItemManager extends AbstractItemManager {
if (itemStack == null || itemStack.getType() == Material.AIR) return "AIR";
String id = provider.itemID(itemStack);
if (id != null) return id;
plugin.debug(() -> "Start checking ID from external plugins");
for (ItemProvider p : itemDetectArray) {
plugin.debug(p::identifier);
id = p.itemID(itemStack);
if (id != null) return p.identifier() + ":" + id;
}
@@ -458,6 +460,8 @@ public class BukkitItemManager extends AbstractItemManager {
}
private void handleInteractEvent(String blockID, WrappedInteractEvent wrapped) {
plugin.debug(() -> "Player [" + wrapped.player().getName() + "] interacted [" + blockID + "] with [" + wrapped.itemID() + "] at " + wrapped.location());
CustomCropsItem customCropsItem = Registries.ITEMS.get(wrapped.itemID());
if (customCropsItem != null) {
InteractionResult result = customCropsItem.interactAt(wrapped);

View File

@@ -134,6 +134,12 @@ public class BukkitWorldManager implements WorldManager, Listener {
}
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
this.worldSettings.clear();
}
private void loadConfig() {
YamlDocument config = BukkitConfigManager.getMainConfig();
@@ -170,12 +176,6 @@ public class BukkitWorldManager implements WorldManager, Listener {
}
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
this.worldSettings.clear();
}
@Override
public void disable() {
this.unload();