9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-25 09:59:20 +00:00
This commit is contained in:
XiaoMoMi
2024-08-31 22:57:45 +08:00
parent 3cbd1f65a6
commit bbde4ebd47
475 changed files with 24733 additions and 24341 deletions

View File

@@ -1,192 +0,0 @@
/*
* Copyright (C) <2022> <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;
import net.momirealms.antigrieflib.AntiGriefLib;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.event.CustomCropsReloadEvent;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.manager.CoolDownManager;
import net.momirealms.customcrops.api.util.EventUtils;
import net.momirealms.customcrops.api.util.LogUtils;
import net.momirealms.customcrops.compatibility.IntegrationManagerImpl;
import net.momirealms.customcrops.libraries.classpath.ReflectionClassPathAppender;
import net.momirealms.customcrops.libraries.dependencies.Dependency;
import net.momirealms.customcrops.libraries.dependencies.DependencyManager;
import net.momirealms.customcrops.libraries.dependencies.DependencyManagerImpl;
import net.momirealms.customcrops.manager.*;
import net.momirealms.customcrops.mechanic.action.ActionManagerImpl;
import net.momirealms.customcrops.mechanic.condition.ConditionManagerImpl;
import net.momirealms.customcrops.mechanic.item.ItemManagerImpl;
import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory;
import net.momirealms.customcrops.mechanic.misc.migrator.Migration;
import net.momirealms.customcrops.mechanic.requirement.RequirementManagerImpl;
import net.momirealms.customcrops.mechanic.world.WorldManagerImpl;
import net.momirealms.customcrops.scheduler.SchedulerImpl;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.List;
public class CustomCropsPluginImpl extends CustomCropsPlugin {
private DependencyManager dependencyManager;
private PacketManager packetManager;
private CommandManager commandManager;
private HologramManager hologramManager;
@Override
public void onLoad() {
this.versionManager = new VersionManagerImpl(this);
this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader()));
this.dependencyManager.loadDependencies(new ArrayList<>(
List.of(
Dependency.GSON,
Dependency.EXP4J,
Dependency.SLF4J_API,
Dependency.SLF4J_SIMPLE,
versionManager.isMojmap() ? Dependency.COMMAND_API_MOJMAP : Dependency.COMMAND_API,
Dependency.BOOSTED_YAML,
Dependency.BSTATS_BASE,
Dependency.BSTATS_BUKKIT
)
));
}
@Override
public void onEnable() {
instance = this;
this.adventure = new AdventureManagerImpl(this);
this.scheduler = new SchedulerImpl(this);
this.configManager = new ConfigManagerImpl(this);
this.integrationManager = new IntegrationManagerImpl(this);
this.conditionManager = new ConditionManagerImpl(this);
this.actionManager = new ActionManagerImpl(this);
this.requirementManager = new RequirementManagerImpl(this);
this.coolDownManager = new CoolDownManager(this);
this.worldManager = new WorldManagerImpl(this);
this.itemManager = new ItemManagerImpl(this,
AntiGriefLib.builder(this)
.silentLogs(true)
.ignoreOP(true)
.build()
);
this.messageManager = new MessageManagerImpl(this);
this.packetManager = new PacketManager(this);
this.commandManager = new CommandManager(this);
this.placeholderManager = new PlaceholderManagerImpl(this);
this.hologramManager = new HologramManager(this);
this.commandManager.init();
try {
this.integrationManager.init();
} catch (Exception e) {
e.printStackTrace();
}
BukkitItemFactory.create(this);
Migration.tryUpdating();
this.reload();
if (ConfigManager.metrics()) new Metrics(this, 16593);
if (ConfigManager.checkUpdate()) {
this.versionManager.checkUpdate().thenAccept(result -> {
if (!result) this.getAdventure().sendConsoleMessage("[CustomCrops] You are using the latest version.");
else this.getAdventure().sendConsoleMessage("[CustomCrops] Update is available: <u>https://polymart.org/resource/2625<!u>");
});
}
}
@Override
public void onDisable() {
if (this.commandManager != null) this.commandManager.disable();
if (this.adventure != null) this.adventure.disable();
if (this.requirementManager != null) this.requirementManager.disable();
if (this.actionManager != null) this.actionManager.disable();
if (this.worldManager != null) this.worldManager.disable();
if (this.itemManager != null) this.itemManager.disable();
if (this.conditionManager != null) this.conditionManager.disable();
if (this.coolDownManager != null) this.coolDownManager.disable();
if (this.placeholderManager != null) this.placeholderManager.disable();
if (this.scheduler != null) ((SchedulerImpl) scheduler).shutdown();
instance = null;
}
@Override
public void reload() {
this.configManager.reload();
this.messageManager.reload();
this.itemManager.reload();
this.worldManager.reload();
this.actionManager.reload();
this.requirementManager.reload();
this.conditionManager.reload();
this.coolDownManager.reload();
this.placeholderManager.reload();
this.hologramManager.reload();
((SchedulerImpl) scheduler).reload();
EventUtils.fireAndForget(new CustomCropsReloadEvent(this));
}
@Override
public void debug(String debug) {
if (ConfigManager.debug()) {
LogUtils.info(debug);
}
}
public DependencyManager getDependencyManager() {
return dependencyManager;
}
public PacketManager getPacketManager() {
return packetManager;
}
public HologramManager getHologramManager() {
return hologramManager;
}
@Override
public boolean isHookedPluginEnabled(String plugin) {
return Bukkit.getPluginManager().isPluginEnabled(plugin);
}
@Override
public boolean isHookedPluginEnabled(String hooked, String... versionPrefix) {
Plugin p = Bukkit.getPluginManager().getPlugin(hooked);
if (p != null) {
String ver = p.getDescription().getVersion();
for (String prefix : versionPrefix) {
if (ver.startsWith(prefix)) {
return true;
}
}
}
return false;
}
@Override
public boolean doesHookedPluginExist(String plugin) {
return Bukkit.getPluginManager().getPlugin(plugin) != null;
}
@Override
public String getServerVersion() {
return Bukkit.getServer().getBukkitVersion().split("-")[0];
}
}

View File

@@ -15,21 +15,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.util;
package net.momirealms.customcrops.bukkit;
import org.bukkit.Particle;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import org.bukkit.plugin.java.JavaPlugin;
public class ParticleUtils {
public class BukkitBootstrap extends JavaPlugin {
public static Particle getParticle(String particle) {
try {
return Particle.valueOf(particle);
} catch (IllegalArgumentException e) {
return switch (particle) {
case "REDSTONE" -> Particle.valueOf("DUST");
case "VILLAGER_HAPPY" -> Particle.valueOf("HAPPY_VILLAGER");
default -> Particle.valueOf(particle);
};
}
private BukkitCustomCropsPlugin plugin;
@Override
public void onLoad() {
this.plugin = new BukkitCustomCropsPluginImpl(this);
this.plugin.load();
}
@Override
public void onEnable() {
this.plugin.enable();
}
@Override
public void onDisable() {
this.plugin.disable();
}
}

View File

@@ -0,0 +1,223 @@
package net.momirealms.customcrops.bukkit;
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.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.event.CustomCropsReloadEvent;
import net.momirealms.customcrops.api.misc.cooldown.CoolDownManager;
import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager;
import net.momirealms.customcrops.api.util.EventUtils;
import net.momirealms.customcrops.bukkit.action.BlockActionManager;
import net.momirealms.customcrops.bukkit.action.PlayerActionManager;
import net.momirealms.customcrops.bukkit.command.BukkitCommandManager;
import net.momirealms.customcrops.bukkit.config.BukkitConfigManager;
import net.momirealms.customcrops.bukkit.integration.BukkitIntegrationManager;
import net.momirealms.customcrops.bukkit.item.BukkitItemManager;
import net.momirealms.customcrops.bukkit.misc.HologramManager;
import net.momirealms.customcrops.bukkit.requirement.BlockRequirementManager;
import net.momirealms.customcrops.bukkit.requirement.PlayerRequirementManager;
import net.momirealms.customcrops.bukkit.scheduler.BukkitSchedulerAdapter;
import net.momirealms.customcrops.bukkit.sender.BukkitSenderFactory;
import net.momirealms.customcrops.bukkit.world.BukkitWorldManager;
import net.momirealms.customcrops.common.config.ConfigLoader;
import net.momirealms.customcrops.common.dependency.Dependency;
import net.momirealms.customcrops.common.dependency.DependencyManagerImpl;
import net.momirealms.customcrops.common.helper.VersionHelper;
import net.momirealms.customcrops.common.locale.TranslationManager;
import net.momirealms.customcrops.common.plugin.classpath.ClassPathAppender;
import net.momirealms.customcrops.common.plugin.classpath.ReflectionClassPathAppender;
import net.momirealms.customcrops.common.plugin.feature.Reloadable;
import net.momirealms.customcrops.common.plugin.logging.JavaPluginLogger;
import net.momirealms.customcrops.common.plugin.logging.PluginLogger;
import net.momirealms.sparrow.heart.SparrowHeart;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Consumer;
public class BukkitCustomCropsPluginImpl extends BukkitCustomCropsPlugin {
private final ClassPathAppender classPathAppender;
private final PluginLogger logger;
private BukkitCommandManager commandManager;
private HologramManager hologramManager;
private Consumer<Object> debugger;
private String buildByBit = "%%__BUILTBYBIT__%%";
private String polymart = "%%__POLYMART__%%";
private String time = "%%__TIMESTAMP__%%";
private String user = "%%__USER__%%";
private String username = "%%__USERNAME__%%";
public BukkitCustomCropsPluginImpl(Plugin boostrap) {
super(boostrap);
VersionHelper.init(getServerVersion());
this.scheduler = new BukkitSchedulerAdapter(this);
this.logger = new JavaPluginLogger(getBoostrap().getLogger());
this.classPathAppender = new ReflectionClassPathAppender(this);
this.dependencyManager = new DependencyManagerImpl(this);
this.registryAccess = new SimpleRegistryAccess(this);
}
@Override
public void debug(Object message) {
if (this.debugger != null)
this.debugger.accept(message);
}
@Override
public InputStream getResourceStream(String filePath) {
return getBoostrap().getResource(filePath);
}
@Override
public PluginLogger getPluginLogger() {
return logger;
}
@Override
public ClassPathAppender getClassPathAppender() {
return classPathAppender;
}
@Override
public Path getDataDirectory() {
return getBoostrap().getDataFolder().toPath().toAbsolutePath();
}
@Override
public ConfigLoader getConfigManager() {
return configManager;
}
@Override
public String getServerVersion() {
return Bukkit.getServer().getBukkitVersion().split("-")[0];
}
@SuppressWarnings("deprecation")
@Override
public String getPluginVersion() {
return getBoostrap().getDescription().getVersion();
}
@Override
public void load() {
this.dependencyManager.loadDependencies(
List.of(
Dependency.BOOSTED_YAML,
Dependency.BSTATS_BASE, Dependency.BSTATS_BUKKIT,
Dependency.CAFFEINE,
Dependency.GEANTY_REF,
Dependency.CLOUD_CORE, Dependency.CLOUD_SERVICES, Dependency.CLOUD_BUKKIT, Dependency.CLOUD_PAPER, Dependency.CLOUD_BRIGADIER, Dependency.CLOUD_MINECRAFT_EXTRAS,
Dependency.GSON,
Dependency.EXP4J,
Dependency.ZSTD
)
);
this.registerDefaultMechanics();
}
@Override
public void enable() {
SparrowHeart.getInstance();
this.configManager = new BukkitConfigManager(this);
super.requirementManagers.put(Player.class, new PlayerRequirementManager(this));
super.requirementManagers.put(CustomCropsBlockState.class, new BlockRequirementManager(this));
super.actionManagers.put(Player.class, new PlayerActionManager(this));
super.actionManagers.put(CustomCropsBlockState.class, new BlockActionManager(this));
this.translationManager = new TranslationManager(this);
this.senderFactory = new BukkitSenderFactory(this);
this.itemManager = new BukkitItemManager(this);
this.integrationManager = new BukkitIntegrationManager(this);
this.placeholderManager = new BukkitPlaceholderManager(this);
this.coolDownManager = new CoolDownManager(this);
this.worldManager = new BukkitWorldManager(this);
this.hologramManager = new HologramManager(this);
this.commandManager = new BukkitCommandManager(this);
this.commandManager.registerDefaultFeatures();
boolean downloadFromPolymart = polymart.equals("1");
boolean downloadFromBBB = buildByBit.equals("true");
this.getScheduler().sync().runLater(() -> {
this.reload();
((SimpleRegistryAccess) registryAccess).freeze();
if (ConfigManager.metrics()) new Metrics((JavaPlugin) getBoostrap(), 16593);
if (ConfigManager.checkUpdate()) {
VersionHelper.UPDATE_CHECKER.apply(this).thenAccept(result -> {
String link;
if (downloadFromPolymart) {
link = "https://polymart.org/resource/2625/";
} else if (downloadFromBBB) {
link = "https://builtbybit.com/resources/36363/";
} else {
link = "https://github.com/Xiao-MoMi/Custom-Crops/";
}
if (!result) {
this.getPluginLogger().info("You are using the latest version.");
} else {
this.getPluginLogger().warn("Update is available: " + link);
}
});
}
}, 1, null);
}
@Override
public void disable() {
this.worldManager.disable();
this.placeholderManager.disable();
this.hologramManager.disable();
this.integrationManager.disable();
this.coolDownManager.disable();
this.commandManager.unregisterFeatures();
}
@Override
public void reload() {
this.worldManager.unload();
this.configManager.reload();
this.debugger = ConfigManager.debug() ? (s) -> logger.info("[DEBUG] " + s.toString()) : (s) -> {};
this.coolDownManager.reload();
this.placeholderManager.reload();
this.translationManager.reload();
this.hologramManager.reload();
this.actionManagers.values().forEach(Reloadable::reload);
this.requirementManagers.values().forEach(Reloadable::reload);
this.worldManager.load();
EventUtils.fireAndForget(new CustomCropsReloadEvent(this));
}
private void registerDefaultMechanics() {
registryAccess.registerFertilizerType(FertilizerType.SPEED_GROW);
registryAccess.registerFertilizerType(FertilizerType.QUALITY);
registryAccess.registerFertilizerType(FertilizerType.SOIL_RETAIN);
registryAccess.registerFertilizerType(FertilizerType.VARIATION);
registryAccess.registerFertilizerType(FertilizerType.YIELD_INCREASE);
registryAccess.registerBlockMechanic(new CropBlock());
registryAccess.registerBlockMechanic(new PotBlock());
registryAccess.registerBlockMechanic(new ScarecrowBlock());
registryAccess.registerBlockMechanic(new SprinklerBlock());
registryAccess.registerBlockMechanic(new GreenhouseBlock());
registryAccess.registerItemMechanic(new SeedItem());
registryAccess.registerItemMechanic(new WateringCanItem());
registryAccess.registerItemMechanic(new FertilizerItem());
registryAccess.registerItemMechanic(new SprinklerItem());
}
}

View File

@@ -0,0 +1,110 @@
package net.momirealms.customcrops.bukkit.action;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.action.AbstractActionManager;
import net.momirealms.customcrops.api.action.Action;
import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.CustomForm;
import net.momirealms.customcrops.api.core.ExistenceForm;
import net.momirealms.customcrops.api.core.FurnitureRotation;
import net.momirealms.customcrops.api.core.block.CropBlock;
import net.momirealms.customcrops.api.core.block.PotBlock;
import net.momirealms.customcrops.api.core.block.VariationData;
import net.momirealms.customcrops.api.core.item.Fertilizer;
import net.momirealms.customcrops.api.core.item.FertilizerConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsChunk;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;
import org.bukkit.Location;
import java.util.*;
import static java.util.Objects.requireNonNull;
public class BlockActionManager extends AbstractActionManager<CustomCropsBlockState> {
public BlockActionManager(BukkitCustomCropsPlugin plugin) {
super(plugin);
}
@Override
public void load() {
loadExpansions(CustomCropsBlockState.class);
}
@Override
protected void registerBuiltInActions() {
super.registerBuiltInActions();
super.registerBundleAction(CustomCropsBlockState.class);
this.registerVariationAction();
}
private void registerVariationAction() {
registerAction((args, chance) -> {
if (args instanceof Section section) {
boolean ignore = section.getBoolean("ignore-fertilizer", false);
List<VariationData> variationDataList = new ArrayList<>();
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section inner) {
VariationData variationData = new VariationData(
inner.getString("item"),
CustomForm.valueOf(inner.getString("type", "TripWire").toUpperCase(Locale.ENGLISH)).existenceForm(),
inner.getDouble("chance")
);
variationDataList.add(variationData);
}
}
VariationData[] variations = variationDataList.toArray(new VariationData[0]);
return context -> {
if (Math.random() > chance) return;
if (!(context.holder().type() instanceof CropBlock cropBlock)) {
return;
}
Fertilizer[] fertilizers = null;
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
Optional<CustomCropsWorld<?>> world = plugin.getWorldManager().getWorld(location.getWorld());
if (world.isEmpty()) {
return;
}
Pos3 pos3 = Pos3.from(location);
if (!ignore) {
Pos3 potLocation = pos3.add(0, -1, 0);
Optional<CustomCropsChunk> chunk = world.get().getChunk(potLocation.toChunkPos());
if (chunk.isPresent()) {
Optional<CustomCropsBlockState> state = chunk.get().getBlockState(potLocation);
if (state.isPresent()) {
if (state.get().type() instanceof PotBlock potBlock) {
fertilizers = potBlock.fertilizers(state.get());
}
}
}
}
ArrayList<FertilizerConfig> configs = new ArrayList<>();
if (fertilizers != null) {
for (Fertilizer fertilizer : fertilizers) {
Optional.ofNullable(fertilizer.config()).ifPresent(configs::add);
}
}
for (VariationData variationData : variations) {
double variationChance = variationData.chance();
for (FertilizerConfig fertilizer : configs) {
variationChance = fertilizer.processVariationChance(variationChance);
}
if (Math.random() < variationChance) {
plugin.getItemManager().remove(location, ExistenceForm.ANY);
world.get().removeBlockState(pos3);
plugin.getItemManager().place(location, variationData.existenceForm(), variationData.id(), FurnitureRotation.random());
cropBlock.fixOrGetState(world.get(), pos3, variationData.id());
break;
}
}
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at variation action which is expected to be `Section`");
return Action.empty();
}
}, "variation");
}
}

View File

@@ -0,0 +1,524 @@
package net.momirealms.customcrops.bukkit.action;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.action.AbstractActionManager;
import net.momirealms.customcrops.api.action.Action;
import net.momirealms.customcrops.api.context.ContextKeys;
import net.momirealms.customcrops.api.core.block.CropBlock;
import net.momirealms.customcrops.api.core.block.CropConfig;
import net.momirealms.customcrops.api.core.block.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.misc.placeholder.BukkitPlaceholderManager;
import net.momirealms.customcrops.api.misc.value.MathValue;
import net.momirealms.customcrops.api.misc.value.TextValue;
import net.momirealms.customcrops.api.util.LocationUtils;
import net.momirealms.customcrops.api.util.PlayerUtils;
import net.momirealms.customcrops.bukkit.integration.VaultHook;
import net.momirealms.customcrops.bukkit.misc.HologramManager;
import net.momirealms.customcrops.common.helper.AdventureHelper;
import net.momirealms.customcrops.common.util.ListUtils;
import net.momirealms.customcrops.common.util.RandomUtils;
import net.momirealms.sparrow.heart.SparrowHeart;
import net.momirealms.sparrow.heart.feature.inventory.HandSlot;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.util.*;
import static java.util.Objects.requireNonNull;
public class PlayerActionManager extends AbstractActionManager<Player> {
public PlayerActionManager(BukkitCustomCropsPlugin plugin) {
super(plugin);
}
@Override
public void load() {
loadExpansions(Player.class);
}
@Override
protected void registerBuiltInActions() {
super.registerBuiltInActions();
super.registerBundleAction(Player.class);
this.registerPlayerCommandAction();
this.registerCloseInvAction();
this.registerActionBarAction();
this.registerExpAction();
this.registerFoodAction();
this.registerItemAction();
this.registerMoneyAction();
this.registerPotionAction();
this.registerSoundAction();
this.registerPluginExpAction();
this.registerTitleAction();
this.registerSwingHandAction();
this.registerForceTickAction();
this.registerHologramAction();
this.registerMessageAction();
}
private void registerMessageAction() {
registerAction((args, chance) -> {
List<String> messages = ListUtils.toList(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
List<String> replaced = plugin.getPlaceholderManager().parse(context.holder(), messages, context.placeholderMap());
Audience audience = plugin.getSenderFactory().getAudience(context.holder());
for (String text : replaced) {
audience.sendMessage(AdventureHelper.miniMessage(text));
}
};
}, "message");
registerAction((args, chance) -> {
List<String> messages = ListUtils.toList(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
String random = messages.get(RandomUtils.generateRandomInt(0, messages.size() - 1));
random = BukkitPlaceholderManager.getInstance().parse(context.holder(), random, context.placeholderMap());
Audience audience = plugin.getSenderFactory().getAudience(context.holder());
audience.sendMessage(AdventureHelper.miniMessage(random));
};
}, "random-message");
}
private void registerPlayerCommandAction() {
registerAction((args, chance) -> {
List<String> commands = ListUtils.toList(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
List<String> replaced = BukkitPlaceholderManager.getInstance().parse(context.holder(), commands, context.placeholderMap());
plugin.getScheduler().sync().run(() -> {
for (String text : replaced) {
context.holder().performCommand(text);
}
}, context.holder().getLocation());
};
}, "player-command");
}
private void registerCloseInvAction() {
registerAction((args, chance) -> context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
context.holder().closeInventory();
}, "close-inv");
}
private void registerActionBarAction() {
registerAction((args, chance) -> {
String text = (String) args;
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Audience audience = plugin.getSenderFactory().getAudience(context.holder());
Component component = AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(context.holder(), text, context.placeholderMap()));
audience.sendActionBar(component);
};
}, "actionbar");
registerAction((args, chance) -> {
List<String> texts = ListUtils.toList(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
String random = texts.get(RandomUtils.generateRandomInt(0, texts.size() - 1));
random = plugin.getPlaceholderManager().parse(context.holder(), random, context.placeholderMap());
Audience audience = plugin.getSenderFactory().getAudience(context.holder());
audience.sendActionBar(AdventureHelper.miniMessage(random));
};
}, "random-actionbar");
}
private void registerExpAction() {
registerAction((args, chance) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
final Player player = context.holder();
ExperienceOrb entity = player.getLocation().getWorld().spawn(player.getLocation().clone().add(0,0.5,0), ExperienceOrb.class);
entity.setExperience((int) value.evaluate(context));
};
}, "mending");
registerAction((args, chance) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
final Player player = context.holder();
player.giveExp((int) Math.round(value.evaluate(context)));
Audience audience = plugin.getSenderFactory().getAudience(player);
AdventureHelper.playSound(audience, Sound.sound(Key.key("minecraft:entity.experience_orb.pickup"), Sound.Source.PLAYER, 1, 1));
};
}, "exp");
registerAction((args, chance) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Player player = context.holder();
player.setLevel((int) Math.max(0, player.getLevel() + value.evaluate(context)));
};
}, "level");
}
private void registerFoodAction() {
registerAction((args, chance) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Player player = context.holder();
player.setFoodLevel((int) (player.getFoodLevel() + value.evaluate(context)));
};
}, "food");
registerAction((args, chance) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Player player = context.holder();
player.setSaturation((float) (player.getSaturation() + value.evaluate(context)));
};
}, "saturation");
}
private void registerItemAction() {
registerAction((args, chance) -> {
if (args instanceof Section section) {
boolean mainOrOff = section.getString("hand", "main").equalsIgnoreCase("main");
int amount = section.getInt("amount", 1);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Player player = context.holder();
boolean tempHand = mainOrOff;
EquipmentSlot hand = context.arg(ContextKeys.SLOT);
if (hand == EquipmentSlot.OFF_HAND || hand == EquipmentSlot.HAND) {
tempHand = hand == EquipmentSlot.HAND;
}
ItemStack itemStack = tempHand ? player.getInventory().getItemInMainHand() : player.getInventory().getItemInOffHand();
itemStack.setAmount(Math.max(0, itemStack.getAmount() + amount));
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at item-amount action which is expected to be `Section`");
return Action.empty();
}
}, "item-amount");
registerAction((args, chance) -> {
int amount;
EquipmentSlot slot;
if (args instanceof Integer integer) {
slot = null;
amount = integer;
} else if (args instanceof Section section) {
slot = Optional.ofNullable(section.getString("slot"))
.map(hand -> EquipmentSlot.valueOf(hand.toUpperCase(Locale.ENGLISH)))
.orElse(null);
amount = section.getInt("amount", 1);
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at durability action which is expected to be `Section`");
return Action.empty();
}
return context -> {
if (Math.random() > chance) return;
Player player = context.holder();
if (player == null) return;
EquipmentSlot tempSlot = slot;
EquipmentSlot equipmentSlot = context.arg(ContextKeys.SLOT);
if (equipmentSlot != null) {
tempSlot = equipmentSlot;
}
if (tempSlot == null) {
return;
}
ItemStack itemStack = player.getInventory().getItem(tempSlot);
if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0)
return;
if (itemStack.getItemMeta() == null)
return;
if (amount > 0) {
plugin.getItemManager().decreaseDamage(context.holder(), itemStack, amount);
} else {
plugin.getItemManager().increaseDamage(context.holder(), itemStack, -amount);
}
};
}, "durability");
registerAction((args, chance) -> {
if (args instanceof Section section) {
String id = section.getString("item");
int amount = section.getInt("amount", 1);
boolean toInventory = section.getBoolean("to-inventory", false);
return context -> {
if (Math.random() > chance) return;
Player player = context.holder();
if (player == null) return;
ItemStack itemStack = plugin.getItemManager().build(context.holder(), id);
if (itemStack != null) {
int maxStack = itemStack.getMaxStackSize();
int amountToGive = amount;
while (amountToGive > 0) {
int perStackSize = Math.min(maxStack, amountToGive);
amountToGive -= perStackSize;
ItemStack more = itemStack.clone();
more.setAmount(perStackSize);
if (toInventory) {
PlayerUtils.giveItem(player, more, more.getAmount());
} else {
PlayerUtils.dropItem(player, more, true, true, false);
}
}
}
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at give-item action which is expected to be `Section`");
return Action.empty();
}
}, "give-item");
}
private void registerMoneyAction() {
registerAction((args, chance) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
if (!VaultHook.isHooked()) return;
VaultHook.deposit(context.holder(), value.evaluate(context));
};
}, "give-money");
registerAction((args, chance) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
if (!VaultHook.isHooked()) return;
VaultHook.withdraw(context.holder(), value.evaluate(context));
};
}, "take-money");
}
private void registerPotionAction() {
registerAction((args, chance) -> {
if (args instanceof Section section) {
PotionEffect potionEffect = new PotionEffect(
Objects.requireNonNull(PotionEffectType.getByName(section.getString("type", "BLINDNESS").toUpperCase(Locale.ENGLISH))),
section.getInt("duration", 20),
section.getInt("amplifier", 0)
);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
context.holder().addPotionEffect(potionEffect);
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at potion-effect action which is expected to be `Section`");
return Action.empty();
}
}, "potion-effect");
}
private void registerSoundAction() {
registerAction((args, chance) -> {
if (args instanceof Section section) {
Sound sound = Sound.sound(
Key.key(section.getString("key")),
Sound.Source.valueOf(section.getString("source", "PLAYER").toUpperCase(Locale.ENGLISH)),
section.getDouble("volume", 1.0).floatValue(),
section.getDouble("pitch", 1.0).floatValue()
);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Audience audience = plugin.getSenderFactory().getAudience(context.holder());
AdventureHelper.playSound(audience, sound);
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at sound action which is expected to be `Section`");
return Action.empty();
}
}, "sound");
}
private void registerPluginExpAction() {
registerAction((args, chance) -> {
if (args instanceof Section section) {
String pluginName = section.getString("plugin");
MathValue<Player> value = MathValue.auto(section.get("exp"));
String target = section.getString("target");
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Optional.ofNullable(plugin.getIntegrationManager().getLevelerProvider(pluginName)).ifPresentOrElse(it -> {
it.addXp(context.holder(), target, value.evaluate(context));
}, () -> plugin.getPluginLogger().warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation."));
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plugin-exp action which is expected to be `Section`");
return Action.empty();
}
}, "plugin-exp");
}
private void registerTitleAction() {
registerAction((args, chance) -> {
if (args instanceof Section section) {
TextValue<Player> title = TextValue.auto(section.getString("title", ""));
TextValue<Player> subtitle = TextValue.auto(section.getString("subtitle", ""));
int fadeIn = section.getInt("fade-in", 20);
int stay = section.getInt("stay", 30);
int fadeOut = section.getInt("fade-out", 10);
return context -> {
if (Math.random() > chance) return;
final Player player = context.holder();
if (player == null) return;
Audience audience = plugin.getSenderFactory().getAudience(player);
AdventureHelper.sendTitle(audience,
AdventureHelper.miniMessage(title.render(context)),
AdventureHelper.miniMessage(subtitle.render(context)),
fadeIn, stay, fadeOut
);
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at title action which is expected to be `Section`");
return Action.empty();
}
}, "title");
registerAction((args, chance) -> {
if (args instanceof Section section) {
List<String> titles = section.getStringList("titles");
if (titles.isEmpty()) titles.add("");
List<String> subtitles = section.getStringList("subtitles");
if (subtitles.isEmpty()) subtitles.add("");
int fadeIn = section.getInt("fade-in", 20);
int stay = section.getInt("stay", 30);
int fadeOut = section.getInt("fade-out", 10);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
TextValue<Player> title = TextValue.auto(titles.get(RandomUtils.generateRandomInt(0, titles.size() - 1)));
TextValue<Player> subtitle = TextValue.auto(subtitles.get(RandomUtils.generateRandomInt(0, subtitles.size() - 1)));
final Player player = context.holder();
Audience audience = plugin.getSenderFactory().getAudience(player);
AdventureHelper.sendTitle(audience,
AdventureHelper.miniMessage(title.render(context)),
AdventureHelper.miniMessage(subtitle.render(context)),
fadeIn, stay, fadeOut
);
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at random-title action which is expected to be `Section`");
return Action.empty();
}
}, "random-title");
}
private void registerHologramAction() {
registerAction(((args, chance) -> {
if (args instanceof Section section) {
TextValue<Player> text = TextValue.auto(section.getString("text", ""));
MathValue<Player> duration = MathValue.auto(section.get("duration", 20));
boolean position = section.getString("position", "other").equals("other");
MathValue<Player> x = MathValue.auto(section.get("x", 0));
MathValue<Player> y = MathValue.auto(section.get("y", 0));
MathValue<Player> z = MathValue.auto(section.get("z", 0));
boolean applyCorrection = section.getBoolean("apply-correction", false);
boolean onlyShowToOne = !section.getBoolean("visible-to-all", false);
int range = section.getInt("range", 32);
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Player owner = context.holder();
Location location = position ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : owner.getLocation().clone();
location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context));
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
Pos3 pos3 = Pos3.from(location);
if (applyCorrection) {
Optional<CustomCropsBlockState> optionalState = optionalWorld.get().getBlockState(pos3);
if (optionalState.isPresent()) {
if (optionalState.get().type() instanceof CropBlock cropBlock) {
CropConfig config = cropBlock.config(optionalState.get());
int point = cropBlock.point(optionalState.get());
if (config != null) {
int tempPoints = point;
while (tempPoints >= 0) {
Map.Entry<Integer, CropStageConfig> entry = config.getFloorStageEntry(tempPoints);
CropStageConfig stage = entry.getValue();
if (stage.stageID() != null) {
location.add(0, stage.displayInfoOffset(), 0);
break;
}
tempPoints = stage.point() - 1;
}
}
}
}
}
ArrayList<Player> viewers = new ArrayList<>();
if (onlyShowToOne) {
if (owner == null) return;
viewers.add(owner);
} else {
for (Player player : owner.getWorld().getPlayers()) {
if (LocationUtils.getDistance(player.getLocation(), location) <= range) {
viewers.add(player);
}
}
}
Component component = AdventureHelper.miniMessage(text.render(context));
for (Player viewer : viewers) {
HologramManager.getInstance().showHologram(viewer, location, component, (int) (duration.evaluate(context) * 50));
}
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at hologram action which is expected to be `Section`");
return Action.empty();
}
}), "hologram");
}
private void registerSwingHandAction() {
registerAction((args, chance) -> {
boolean arg = (boolean) args;
return context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
SparrowHeart.getInstance().swingHand(context.holder(), arg ? HandSlot.MAIN : HandSlot.OFF);
};
}, "swing-hand");
}
private void registerForceTickAction() {
registerAction((args, chance) -> context -> {
if (context.holder() == null) return;
if (Math.random() > chance) return;
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
Pos3 pos3 = Pos3.from(location);
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld());
optionalWorld.ifPresent(world -> world.getChunk(pos3.toChunkPos()).flatMap(chunk -> chunk.getBlockState(pos3)).ifPresent(state -> {
state.type().randomTick(state, world, pos3);
state.type().scheduledTick(state, world, pos3);
}));
}, "force-tick");
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.bukkit.command;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.common.command.AbstractCommandFeature;
import net.momirealms.customcrops.common.command.CustomCropsCommandManager;
import net.momirealms.customcrops.common.sender.SenderFactory;
import net.momirealms.customcrops.common.util.Pair;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.incendo.cloud.bukkit.data.Selector;
import java.util.Collection;
public abstract class BukkitCommandFeature<C extends CommandSender> extends AbstractCommandFeature<C> {
public BukkitCommandFeature(CustomCropsCommandManager<C> commandManager) {
super(commandManager);
}
@Override
@SuppressWarnings("unchecked")
protected SenderFactory<?, C> getSenderFactory() {
return (SenderFactory<?, C>) BukkitCustomCropsPlugin.getInstance().getSenderFactory();
}
public Pair<TranslatableComponent.Builder, Component> resolveSelector(Selector<? extends Entity> selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) {
Collection<? extends Entity> entities = selector.values();
if (entities.size() == 1) {
return Pair.of(single, Component.text(entities.iterator().next().getName()));
} else {
return Pair.of(multiple, Component.text(entities.size()));
}
}
public Pair<TranslatableComponent.Builder, Component> resolveSelector(Collection<? extends Entity> selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) {
if (selector.size() == 1) {
return Pair.of(single, Component.text(selector.iterator().next().getName()));
} else {
return Pair.of(multiple, Component.text(selector.size()));
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.bukkit.command;
import net.kyori.adventure.util.Index;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.bukkit.command.feature.DebugDataCommand;
import net.momirealms.customcrops.bukkit.command.feature.ReloadCommand;
import net.momirealms.customcrops.common.command.AbstractCommandManager;
import net.momirealms.customcrops.common.command.CommandFeature;
import net.momirealms.customcrops.common.sender.Sender;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.SenderMapper;
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.paper.LegacyPaperCommandManager;
import org.incendo.cloud.setting.ManagerSetting;
import java.util.List;
public class BukkitCommandManager extends AbstractCommandManager<CommandSender> {
private final List<CommandFeature<CommandSender>> FEATURES = List.of(
new ReloadCommand(this),
new DebugDataCommand(this)
);
private final Index<String, CommandFeature<CommandSender>> INDEX = Index.create(CommandFeature::getFeatureID, FEATURES);
public BukkitCommandManager(BukkitCustomCropsPlugin plugin) {
super(plugin, new LegacyPaperCommandManager<>(
plugin.getBoostrap(),
ExecutionCoordinator.simpleCoordinator(),
SenderMapper.identity()
));
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true);
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
manager.registerBrigadier();
manager.brigadierManager().setNativeNumberSuggestions(true);
} else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
manager.registerAsynchronousCompletions();
}
}
@Override
protected Sender wrapSender(CommandSender sender) {
return ((BukkitCustomCropsPlugin) plugin).getSenderFactory().wrap(sender);
}
@Override
public Index<String, CommandFeature<CommandSender>> getFeatures() {
return INDEX;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.bukkit.command.feature;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.Pos3;
import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature;
import net.momirealms.customcrops.common.command.CustomCropsCommandManager;
import net.momirealms.customcrops.common.helper.AdventureHelper;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.Command;
import org.incendo.cloud.CommandManager;
import java.util.Optional;
public class DebugDataCommand extends BukkitCommandFeature<CommandSender> {
public DebugDataCommand(CustomCropsCommandManager<CommandSender> commandManager) {
super(commandManager);
}
@Override
public Command.Builder<? extends CommandSender> assembleCommand(CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.senderType(Player.class)
.flag(manager.flagBuilder("this").build())
.handler(context -> {
Player player = context.sender();
Location location;
if (context.flags().hasFlag("this")) {
location = player.getLocation();
} else {
Block block = player.getTargetBlockExact(10);
if (block == null) return;
location = block.getLocation();
}
BukkitCustomCropsPlugin.getInstance().getWorldManager().getWorld(location.getWorld()).ifPresent(world -> {
Optional<CustomCropsBlockState> state = world.getBlockState(Pos3.from(location));
if (state.isPresent()) {
BukkitCustomCropsPlugin.getInstance().getSenderFactory().wrap(player)
.sendMessage(AdventureHelper.miniMessage(state.get().toString()));
} else {
BukkitCustomCropsPlugin.getInstance().getSenderFactory().wrap(player)
.sendMessage(AdventureHelper.miniMessage("Data not found"));
}
});
});
}
@Override
public String getFeatureID() {
return "debug_data";
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.bukkit.command.feature;
import net.kyori.adventure.text.Component;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.bukkit.command.BukkitCommandFeature;
import net.momirealms.customcrops.common.command.CustomCropsCommandManager;
import net.momirealms.customcrops.common.locale.MessageConstants;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.Command;
import org.incendo.cloud.CommandManager;
public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
public ReloadCommand(CustomCropsCommandManager<CommandSender> commandManager) {
super(commandManager);
}
@Override
public Command.Builder<? extends CommandSender> assembleCommand(CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.flag(manager.flagBuilder("silent").withAliases("s"))
.handler(context -> {
long time1 = System.currentTimeMillis();
BukkitCustomCropsPlugin.getInstance().reload();
handleFeedback(context, MessageConstants.COMMAND_RELOAD_SUCCESS, Component.text(System.currentTimeMillis() - time1));
});
}
@Override
public String getFeatureID() {
return "reload";
}
}

View File

@@ -0,0 +1,249 @@
package net.momirealms.customcrops.bukkit.config;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning;
import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.common.ScalarStyle;
import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.exceptions.ConstructorException;
import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.nodes.Tag;
import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
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.*;
import net.momirealms.customcrops.api.core.block.CropConfig;
import net.momirealms.customcrops.api.core.block.CropStageConfig;
import net.momirealms.customcrops.api.core.block.PotConfig;
import net.momirealms.customcrops.api.core.block.SprinklerConfig;
import net.momirealms.customcrops.api.core.item.FertilizerConfig;
import net.momirealms.customcrops.api.core.item.WateringCanConfig;
import net.momirealms.customcrops.api.util.PluginUtils;
import net.momirealms.customcrops.common.helper.AdventureHelper;
import net.momirealms.customcrops.common.helper.VersionHelper;
import net.momirealms.customcrops.common.locale.TranslationManager;
import net.momirealms.customcrops.common.plugin.CustomCropsProperties;
import net.momirealms.customcrops.common.util.ListUtils;
import org.bukkit.Bukkit;
import org.bukkit.Particle;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class BukkitConfigManager extends ConfigManager {
private static YamlDocument MAIN_CONFIG;
public static YamlDocument getMainConfig() {
return MAIN_CONFIG;
}
public BukkitConfigManager(BukkitCustomCropsPlugin plugin) {
super(plugin);
}
@Override
public void load() {
String configVersion = CustomCropsProperties.getValue("config");
try (InputStream inputStream = new FileInputStream(resolveConfig("config.yml").toFile())) {
MAIN_CONFIG = YamlDocument.create(
inputStream,
plugin.getResourceStream("config.yml"),
GeneralSettings.builder()
.setRouteSeparator('.')
.setUseDefaults(false)
.build(),
LoaderSettings
.builder()
.setAutoUpdate(true)
.build(),
DumperSettings.builder()
.setScalarFormatter((tag, value, role, def) -> {
if (role == NodeRole.KEY) {
return ScalarStyle.PLAIN;
} else {
return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN;
}
})
.build(),
UpdaterSettings
.builder()
.setVersioning(new BasicVersioning("config-version"))
.addIgnoredRoute(configVersion, "other-settings.placeholder-register", '.')
.build()
);
MAIN_CONFIG.save(resolveConfig("config.yml").toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
this.loadSettings();
this.loadConfigs();
}
private void loadSettings() {
YamlDocument config = getMainConfig();
TranslationManager.forceLocale(TranslationManager.parseLocale(config.getString("force-locale", "")));
AdventureHelper.legacySupport = config.getBoolean("other-settings.legacy-color-code-support", true);
metrics = config.getBoolean("metrics", true);
checkUpdate = config.getBoolean("update-checker", true);
debug = config.getBoolean("debug", false);
protectOriginalLore = config.getBoolean("other-settings.protect-original-lore", false);
doubleCheck = config.getBoolean("other-settings.double-check", false);
enableScarecrow = config.getBoolean("mechanics.scarecrow.enable", true);
scarecrow = new HashSet<>(ListUtils.toList(config.get("mechanics.scarecrow.id")));
scarecrowExistenceForm = CustomForm.valueOf(config.getString("mechanics.scarecrow.type", "ITEM_FRAME")).existenceForm();
scarecrowRange = config.getInt("mechanics.scarecrow.range", 7);
scarecrowProtectChunk = config.getBoolean("mechanics.scarecrow.protect-chunk", false);
enableGreenhouse = config.getBoolean("mechanics.greenhouse.enable", true);
greenhouse = new HashSet<>(ListUtils.toList(config.get("mechanics.greenhouse.id")));
greenhouseExistenceForm = CustomForm.valueOf(config.getString("mechanics.greenhouse.type", "CHORUS")).existenceForm();
greenhouseRange = config.getInt("mechanics.greenhouse.range", 5);
syncSeasons = config.getBoolean("mechanics.sync-season.enable", false);
referenceWorld = config.getString("mechanics.sync-season.reference", "world");
itemDetectOrder = config.getStringList("other-settings.item-detection-order").toArray(new String[0]);
absoluteWorldPath = config.getString("worlds.absolute-world-folder-path");
defaultQualityRatio = getQualityRatio(config.getString("mechanics.default-quality-ratio", "17/2/1"));
hasNamespace = PluginUtils.isEnabled("ItemsAdder");
}
@Override
public void saveResource(String filePath) {
File file = new File(plugin.getDataFolder(), filePath);
if (!file.exists()) {
plugin.getBoostrap().saveResource(filePath, false);
addDefaultNamespace(file);
}
}
@Override
public void unload() {
this.clearConfigs();
}
private void loadConfigs() {
Deque<File> fileDeque = new ArrayDeque<>();
for (ConfigType type : ConfigType.values()) {
File typeFolder = new File(plugin.getDataFolder(), "contents" + File.separator + type.path());
if (!typeFolder.exists()) {
if (!typeFolder.mkdirs()) return;
saveResource("contents" + File.separator + type.path() + File.separator + "default.yml");
}
fileDeque.push(typeFolder);
while (!fileDeque.isEmpty()) {
File file = fileDeque.pop();
File[] files = file.listFiles();
if (files == null) continue;
for (File subFile : files) {
if (subFile.isDirectory()) {
fileDeque.push(subFile);
} else if (subFile.isFile() && subFile.getName().endsWith(".yml")) {
try {
YamlDocument document = plugin.getConfigManager().loadData(subFile);
boolean save = false;
for (Map.Entry<String, Object> entry : document.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section section) {
if (type.parse(this, entry.getKey(), section)) {
save = true;
}
}
}
if (save) {
document.save(subFile);
}
} catch (ConstructorException e) {
plugin.getPluginLogger().warn("Could not load config file: " + subFile.getAbsolutePath() + ". Is it a corrupted file?");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
private void clearConfigs() {
Registries.CROP.clear();
Registries.SEED_TO_CROP.clear();
Registries.STAGE_TO_CROP_UNSAFE.clear();
Registries.SPRINKLER.clear();
Registries.ITEM_TO_SPRINKLER.clear();
Registries.POT.clear();
Registries.ITEM_TO_POT.clear();
Registries.FERTILIZER.clear();
Registries.ITEM_TO_SPRINKLER.clear();
Registries.WATERING_CAN.clear();
Registries.ITEMS.clear();
Registries.BLOCKS.clear();
}
@Override
public void registerWateringCanConfig(WateringCanConfig config) {
Registries.WATERING_CAN.register(config.id(), config);
Registries.ITEMS.register(config.itemID(), BuiltInItemMechanics.WATERING_CAN.mechanic());
}
@Override
public void registerFertilizerConfig(FertilizerConfig config) {
Registries.FERTILIZER.register(config.id(), config);
Registries.ITEM_TO_FERTILIZER.register(config.itemID(), config);
Registries.ITEMS.register(config.itemID(), BuiltInItemMechanics.FERTILIZER.mechanic());
}
@Override
public void registerCropConfig(CropConfig config) {
Registries.CROP.register(config.id(), config);
Registries.SEED_TO_CROP.register(config.seed(), config);
Registries.ITEMS.register(config.seed(), BuiltInItemMechanics.SEED.mechanic());
for (CropStageConfig stageConfig : config.stages()) {
String stageID = stageConfig.stageID();
if (stageID != null) {
List<CropConfig> list = Registries.STAGE_TO_CROP_UNSAFE.get(stageID);
if (list != null) {
list.add(config);
} else {
Registries.STAGE_TO_CROP_UNSAFE.register(stageID, new ArrayList<>(List.of(config)));
Registries.BLOCKS.register(stageID, BuiltInBlockMechanics.CROP.mechanic());
}
}
}
}
@Override
public void registerPotConfig(PotConfig config) {
Registries.POT.register(config.id(), config);
for (String pot : config.blocks()) {
Registries.ITEM_TO_POT.register(pot, config);
Registries.BLOCKS.register(pot, BuiltInBlockMechanics.POT.mechanic());
}
}
@Override
public void registerSprinklerConfig(SprinklerConfig config) {
Registries.SPRINKLER.register(config.id(), config);
for (String id : new HashSet<>(List.of(config.threeDItem(), config.threeDItemWithWater()))) {
Registries.ITEM_TO_SPRINKLER.register(id, config);
Registries.BLOCKS.register(id, BuiltInBlockMechanics.SPRINKLER.mechanic());
}
if (config.twoDItem() != null) {
Registries.ITEM_TO_SPRINKLER.register(config.twoDItem(), config);
Registries.ITEMS.register(config.twoDItem(), BuiltInItemMechanics.SPRINKLER_ITEM.mechanic());
}
}
}

View File

@@ -0,0 +1,320 @@
/*
* 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.bukkit.config;
import com.google.common.base.Preconditions;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.core.ConfigManager;
import net.momirealms.customcrops.api.core.CustomForm;
import net.momirealms.customcrops.api.core.ExistenceForm;
import net.momirealms.customcrops.api.core.Registries;
import net.momirealms.customcrops.api.core.block.CropConfig;
import net.momirealms.customcrops.api.core.block.CropStageConfig;
import net.momirealms.customcrops.api.core.block.PotConfig;
import net.momirealms.customcrops.api.core.block.SprinklerConfig;
import net.momirealms.customcrops.api.core.item.FertilizerConfig;
import net.momirealms.customcrops.api.core.item.FertilizerType;
import net.momirealms.customcrops.api.core.item.WateringCanConfig;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.misc.WaterBar;
import net.momirealms.customcrops.api.misc.value.TextValue;
import net.momirealms.customcrops.api.requirement.RequirementManager;
import net.momirealms.customcrops.common.util.Pair;
import net.momirealms.customcrops.common.util.TriFunction;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
/**
* Configuration types for various mechanics.
*/
public class ConfigType {
public static final ConfigType WATERING_CAN = of(
"watering-cans",
(manager, id, section) -> {
ActionManager<Player> pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class);
WateringCanConfig config = WateringCanConfig.builder()
.id(id)
.itemID(section.getString("item"))
.storage(section.getInt("capacity", 3))
.wateringAmount(section.getInt("water", 1))
.infinite(section.getBoolean("infinite", false))
.width(section.getInt("effective-range.width", 1))
.length(section.getInt("effective-range.length", 1))
.potWhitelist(new HashSet<>(section.getStringList("pot-whitelist")))
.sprinklerWhitelist(new HashSet<>(section.getStringList("sprinkler-whitelist")))
.dynamicLore(section.getBoolean("dynamic-lore"))
.lore(section.getStringList("dynamic-lore.lore").stream().map(TextValue::<Player>auto).toList())
.fillMethods(manager.getFillMethods(section.getSection("fill-method")))
.requirements(BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class).parseRequirements(section.getSection("requirements"), true))
.fullActions(pam.parseActions(section.getSection("events.full")))
.addWaterActions(pam.parseActions(section.getSection("events.add_water")))
.consumeWaterActions(pam.parseActions(section.getSection("events.consume_water")))
.runOutOfWaterActions(pam.parseActions(section.getSection("events.no_water")))
.wrongPotActions(pam.parseActions(section.getSection("events.wrong_pot")))
.wrongSprinklerActions(pam.parseActions(section.getSection("events.wrong_sprinkler")))
.appearances(manager.getInt2IntMap(section.getSection("appearance")))
.waterBar(section.contains("water-bar") ? WaterBar.of(
section.getString("water-bar.left", ""),
section.getString("water-bar.empty", ""),
section.getString("water-bar.full", ""),
section.getString("water-bar.right", "")
) : null)
.build();
manager.registerWateringCanConfig(config);
return false;
}
);
public static final ConfigType FERTILIZER = of(
"fertilizers",
(manager, id, section) -> {
String typeName = Preconditions.checkNotNull(section.getString("type"), "Fertilizer type can't be null").toLowerCase(Locale.ENGLISH);
FertilizerType type = Registries.FERTILIZER_TYPE.get(typeName);
if (type == null) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("Fertilizer type " + typeName + " not found");
return false;
}
FertilizerConfig config = type.parse(manager, id, section);
manager.registerFertilizerConfig(config);
return false;
}
);
public static final ConfigType POT = of(
"pots",
(manager, id, section) -> {
ActionManager<Player> pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class);
ActionManager<CustomCropsBlockState> bam = BukkitCustomCropsPlugin.getInstance().getActionManager(CustomCropsBlockState.class);
RequirementManager<Player> prm = BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class);
PotConfig config = PotConfig.builder()
.id(id)
.isRainDropAccepted(section.getBoolean("absorb-rainwater", false))
.isNearbyWaterAccepted(section.getBoolean("absorb-nearby-water", false))
.maxFertilizers(section.getInt("max-fertilizers", 1))
.basicAppearance(Pair.of(section.getString("base.dry"), section.getString("base.wet")))
.potAppearanceMap(manager.getFertilizedPotMap(section.getSection("fertilized-pots")))
.wateringMethods(manager.getWateringMethods(section.getSection("fill-method")))
.addWaterActions(pam.parseActions(section.getSection("events.add_water")))
.placeActions(pam.parseActions(section.getSection("events.place")))
.breakActions(pam.parseActions(section.getSection("events.break")))
.interactActions(pam.parseActions(section.getSection("events.interact")))
.reachLimitActions(pam.parseActions(section.getSection("events.reach_limit")))
.fullWaterActions(pam.parseActions(section.getSection("events.full")))
.tickActions(bam.parseActions(section.getSection("events.tick")))
.useRequirements(prm.parseRequirements(section.getSection("requirements.use"), true))
.placeRequirements(prm.parseRequirements(section.getSection("requirements.place"), true))
.breakRequirements(prm.parseRequirements(section.getSection("requirements.break"), true))
.waterBar(section.contains("water-bar") ? WaterBar.of(
section.getString("water-bar.left", ""),
section.getString("water-bar.empty", ""),
section.getString("water-bar.full", ""),
section.getString("water-bar.right", "")
) : null)
.build();
manager.registerPotConfig(config);
return false;
}
);
public static final ConfigType CROP = of(
"crops",
(manager, id, section) -> {
ActionManager<Player> pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class);
ActionManager<CustomCropsBlockState> bam = BukkitCustomCropsPlugin.getInstance().getActionManager(CustomCropsBlockState.class);
RequirementManager<Player> prm = BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class);
boolean needUpdate = false;
ExistenceForm form = CustomForm.valueOf(section.getString("type").toUpperCase(Locale.ENGLISH)).existenceForm();
Section growConditionSection = section.getSection("grow-conditions");
if (growConditionSection != null) {
for (Map.Entry<String, Object> entry : growConditionSection.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section inner) {
if (inner.contains("type")) {
needUpdate = true;
break;
}
}
}
}
if (needUpdate) {
section.remove("grow-conditions");
section.set("grow-conditions.default.point", 1);
Section newSection = section.createSection("grow-conditions.default.conditions");
newSection.setValue(growConditionSection.getStoredValue());
}
Section pointSection = section.getSection("points");
if (pointSection == null) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().warn("points section not found in crop[" + id + "]");
return false;
}
ArrayList<CropStageConfig.Builder> builders = new ArrayList<>();
for (Map.Entry<String, Object> entry : pointSection.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section inner) {
int point = Integer.parseInt(entry.getKey());
CropStageConfig.Builder builder = CropStageConfig.builder()
.point(point)
.displayInfoOffset(inner.getDouble("hologram-offset-correction"))
.stageID(inner.getString("model"))
.breakRequirements(prm.parseRequirements(inner.getSection("requirements.break"), true))
.interactRequirements(prm.parseRequirements(inner.getSection("requirements.interact"), true))
.breakActions(pam.parseActions(inner.getSection("events.break")))
.interactActions(pam.parseActions(inner.getSection("events.interact")))
.growActions(bam.parseActions(inner.getSection("events.grow")));
builders.add(builder);
}
}
CropConfig config = CropConfig.builder()
.id(id)
.seed(section.getString("seed"))
.rotation(section.getBoolean("random-rotation", false))
.maxPoints(section.getInt("max-points", 1))
.potWhitelist(new HashSet<>(section.getStringList("pot-whitelist")))
.wrongPotActions(pam.parseActions(section.getSection("events.wrong_pot")))
.plantActions(pam.parseActions(section.getSection("events.plant")))
.breakActions(pam.parseActions(section.getSection("events.break")))
.interactActions(pam.parseActions(section.getSection("events.interact")))
.reachLimitActions(pam.parseActions(section.getSection("events.reach_limit")))
.interactRequirements(prm.parseRequirements(section.getSection("requirements.interact"), true))
.plantRequirements(prm.parseRequirements(section.getSection("requirements.plant"), true))
.breakRequirements(prm.parseRequirements(section.getSection("requirements.break"), true))
.boneMeals(manager.getBoneMeals(section.getSection("custom-bone-meal")))
.deathConditions(manager.getDeathConditions(section.getSection("death-conditions"), form))
.growConditions(manager.getGrowConditions(section.getSection("grow-conditions")))
.stages(builders)
.build();
manager.registerCropConfig(config);
return needUpdate;
}
);
public static final ConfigType SPRINKLER = of(
"sprinklers",
(manager, id, section) -> {
int rangeValue = section.getInt("range",1);
int workingMode = section.getInt("working-mode", 1);
int[][] range;
if (workingMode == 1) {
int blocks = 4 * rangeValue * rangeValue + 4 * rangeValue + 1;
range = new int[blocks][2];
int index = 0;
for (int i = -rangeValue; i <= rangeValue; i++) {
for (int j = -rangeValue; j <= rangeValue; j++) {
range[index++] = new int[]{i, j};
}
}
} else if (workingMode == 2) {
int blocks = (2 * rangeValue * rangeValue) + 2 * rangeValue + 1;
range = new int[blocks][2];
int index = 0;
for (int i = -rangeValue; i <= rangeValue; i++) {
for (int j = -rangeValue; j <= rangeValue; j++) {
if (Math.abs(i) + Math.abs(j) <= rangeValue) {
range[index++] = new int[]{i, j};
}
}
}
} else {
throw new IllegalArgumentException("Unrecognized working mode: " + workingMode);
}
ActionManager<Player> pam = BukkitCustomCropsPlugin.getInstance().getActionManager(Player.class);
ActionManager<CustomCropsBlockState> bam = BukkitCustomCropsPlugin.getInstance().getActionManager(CustomCropsBlockState.class);
RequirementManager<Player> prm = BukkitCustomCropsPlugin.getInstance().getRequirementManager(Player.class);
SprinklerConfig config = SprinklerConfig.builder()
.id(id)
.range(range)
.storage(section.getInt("storage", 4))
.infinite(section.getBoolean("infinite", false))
.twoDItem(section.getString("2D-item"))
.sprinklingAmount(section.getInt("water", 1))
.threeDItem(section.getString("3D-item"))
.threeDItemWithWater(section.getString("3D-item-with-water"))
.wateringMethods(manager.getWateringMethods(section.getSection("fill-method")))
.potWhitelist(new HashSet<>(section.getStringList("pot-whitelist")))
.existenceForm(CustomForm.valueOf(section.getString("type", "ITEM_FRAME").toUpperCase(Locale.ENGLISH)).existenceForm())
.addWaterActions(pam.parseActions(section.getSection("events.add_water")))
.breakActions(pam.parseActions(section.getSection("events.break")))
.placeActions(pam.parseActions(section.getSection("events.place")))
.fullWaterActions(pam.parseActions(section.getSection("events.full")))
.reachLimitActions(pam.parseActions(section.getSection("events.reach_limit")))
.interactActions(pam.parseActions(section.getSection("events.interact")))
.workActions(bam.parseActions(section.getSection("events.work")))
.useRequirements(prm.parseRequirements(section.getSection("requirements.use"), true))
.placeRequirements(prm.parseRequirements(section.getSection("requirements.place"), true))
.breakRequirements(prm.parseRequirements(section.getSection("requirements.break"), true))
.waterBar(section.contains("water-bar") ? WaterBar.of(
section.getString("water-bar.left", ""),
section.getString("water-bar.empty", ""),
section.getString("water-bar.full", ""),
section.getString("water-bar.right", "")
) : null)
.build();
manager.registerSprinklerConfig(config);
return false;
}
);
private static final ConfigType[] values = new ConfigType[] {CROP, SPRINKLER, WATERING_CAN, POT, FERTILIZER};
public static ConfigType[] values() {
return values;
}
private final String path;
private final TriFunction<ConfigManager, String, Section, Boolean> argumentConsumer;
public ConfigType(String path, TriFunction<ConfigManager, String, Section, Boolean> argumentConsumer) {
this.path = path;
this.argumentConsumer = argumentConsumer;
}
public static ConfigType of(String path, TriFunction<ConfigManager, String, Section, Boolean> argumentConsumer) {
return new ConfigType(path, argumentConsumer);
}
public String path() {
return path;
}
public boolean parse(ConfigManager manager, String id, Section section) {
return argumentConsumer.apply(manager, id, section);
}
}

View File

@@ -0,0 +1,164 @@
/*
* 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.bukkit.integration;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.integration.IntegrationManager;
import net.momirealms.customcrops.api.integration.ItemProvider;
import net.momirealms.customcrops.api.integration.LevelerProvider;
import net.momirealms.customcrops.api.integration.SeasonProvider;
import net.momirealms.customcrops.bukkit.integration.item.*;
import net.momirealms.customcrops.bukkit.integration.level.*;
import net.momirealms.customcrops.bukkit.integration.papi.CustomCropsPapi;
import net.momirealms.customcrops.bukkit.integration.season.AdvancedSeasonsProvider;
import net.momirealms.customcrops.bukkit.integration.season.RealisticSeasonsProvider;
import net.momirealms.customcrops.bukkit.item.BukkitItemManager;
import net.momirealms.customcrops.bukkit.world.BukkitWorldManager;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
public class BukkitIntegrationManager implements IntegrationManager {
private final BukkitCustomCropsPlugin plugin;
private final HashMap<String, LevelerProvider> levelerProviders = new HashMap<>();
public BukkitIntegrationManager(BukkitCustomCropsPlugin plugin) {
this.plugin = plugin;
try {
this.load();
} catch (Exception e) {
plugin.getPluginLogger().warn("Failed to load integrations", e);
}
}
@Override
public void disable() {
this.levelerProviders.clear();
}
@Override
public void load() {
if (isHooked("MMOItems")) {
registerItemProvider(new MMOItemsItemProvider());
}
if (isHooked("Zaphkiel")) {
registerItemProvider(new ZaphkielItemProvider());
}
if (isHooked("NeigeItems")) {
registerItemProvider(new NeigeItemsItemProvider());
}
if (isHooked("CustomFishing", "2.2", "2.3", "2.4")) {
registerItemProvider(new CustomFishingItemProvider());
}
if (isHooked("MythicMobs", "5")) {
registerItemProvider(new MythicMobsItemProvider());
}
if (isHooked("EcoJobs")) {
registerLevelerProvider(new EcoJobsLevelerProvider());
}
if (isHooked("EcoSkills")) {
registerLevelerProvider(new EcoSkillsLevelerProvider());
}
if (isHooked("Jobs")) {
registerLevelerProvider(new JobsRebornLevelerProvider());
}
if (isHooked("MMOCore")) {
registerLevelerProvider(new MMOCoreLevelerProvider());
}
if (isHooked("mcMMO")) {
registerLevelerProvider(new McMMOLevelerProvider());
}
if (isHooked("AureliumSkills")) {
registerLevelerProvider(new AureliumSkillsProvider());
}
if (isHooked("AuraSkills")) {
registerLevelerProvider(new AuraSkillsLevelerProvider());
}
if (isHooked("RealisticSeasons")) {
registerSeasonProvider(new RealisticSeasonsProvider());
} else if (isHooked("AdvancedSeasons", "1.4", "1.5", "1.6")) {
registerSeasonProvider(new AdvancedSeasonsProvider());
}
if (isHooked("Vault")) {
VaultHook.init();
}
if (isHooked("PlaceholderAPI")) {
new CustomCropsPapi(plugin).load();
}
}
private boolean isHooked(String hooked) {
if (Bukkit.getPluginManager().getPlugin(hooked) != null) {
plugin.getPluginLogger().info(hooked + " hooked!");
return true;
}
return false;
}
@SuppressWarnings("deprecation")
private boolean isHooked(String hooked, String... versionPrefix) {
Plugin p = Bukkit.getPluginManager().getPlugin(hooked);
if (p != null) {
String ver = p.getDescription().getVersion();
for (String prefix : versionPrefix) {
if (ver.startsWith(prefix)) {
plugin.getPluginLogger().info(hooked + " hooked!");
return true;
}
}
}
return false;
}
@Override
public boolean registerLevelerProvider(@NotNull LevelerProvider leveler) {
if (levelerProviders.containsKey(leveler.identifier())) return false;
levelerProviders.put(leveler.identifier(), leveler);
return true;
}
@Override
public boolean unregisterLevelerProvider(@NotNull String id) {
return levelerProviders.remove(id) != null;
}
@Override
@Nullable
public LevelerProvider getLevelerProvider(String plugin) {
return levelerProviders.get(plugin);
}
@Override
public void registerSeasonProvider(@NotNull SeasonProvider season) {
((BukkitWorldManager) plugin.getWorldManager()).seasonProvider(season);
}
@Override
public boolean registerItemProvider(@NotNull ItemProvider item) {
return ((BukkitItemManager) plugin.getItemManager()).registerItemProvider(item);
}
@Override
public boolean unregisterItemProvider(@NotNull String id) {
return ((BukkitItemManager) plugin.getItemManager()).unregisterItemProvider(id);
}
}

View File

@@ -0,0 +1,424 @@
package net.momirealms.customcrops.bukkit.integration.adaptor;
import com.flowpowered.nbt.CompoundMap;
import com.flowpowered.nbt.CompoundTag;
import com.flowpowered.nbt.stream.NBTInputStream;
import com.flowpowered.nbt.stream.NBTOutputStream;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.ConfigManager;
import net.momirealms.customcrops.api.core.Registries;
import net.momirealms.customcrops.api.core.block.CustomCropsBlock;
import net.momirealms.customcrops.api.core.world.*;
import net.momirealms.customcrops.api.core.world.adaptor.AbstractWorldAdaptor;
import net.momirealms.customcrops.api.util.StringUtils;
import net.momirealms.customcrops.common.helper.GsonHelper;
import net.momirealms.customcrops.common.helper.VersionHelper;
import net.momirealms.customcrops.common.util.Key;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Function;
public class BukkitWorldAdaptor extends AbstractWorldAdaptor<World> {
private static BiFunction<World, RegionPos, File> regionFileProvider;
private static Function<World, File> worldFolderProvider;
private static final NamespacedKey WORLD_DATA = new NamespacedKey(BukkitCustomCropsPlugin.getInstance().getBoostrap(), "data");
private static final String DATA_FILE = "customcrops.dat";
public BukkitWorldAdaptor() {
worldFolderProvider = (world -> {
if (ConfigManager.absoluteWorldPath().isEmpty()) {
return world.getWorldFolder();
} else {
return new File(ConfigManager.absoluteWorldPath(), world.getName());
}
});
regionFileProvider = (world, pos) -> new File(worldFolderProvider.apply(world), "customcrops" + File.separator + getRegionDataFile(pos));
}
public static void regionFileProvider(BiFunction<World, RegionPos, File> regionFileProvider) {
BukkitWorldAdaptor.regionFileProvider = regionFileProvider;
}
public static void worldFolderProvider(Function<World, File> worldFolderProvider) {
BukkitWorldAdaptor.worldFolderProvider = worldFolderProvider;
}
@Override
public World getWorld(String worldName) {
return Bukkit.getWorld(worldName);
}
@Override
public CustomCropsWorld<World> adapt(Object world) {
return CustomCropsWorld.create((World) world, this);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@Override
public WorldExtraData loadExtraData(World world) {
if (VersionHelper.isVersionNewerThan1_18()) {
// init world basic info
String json = world.getPersistentDataContainer().get(WORLD_DATA, PersistentDataType.STRING);
WorldExtraData data = (json == null || json.equals("null")) ? WorldExtraData.empty() : GsonHelper.get().fromJson(json, WorldExtraData.class);
if (data == null) data = WorldExtraData.empty();
return data;
} else {
File data = new File(getWorldFolder(world), DATA_FILE);
if (data.exists()) {
byte[] fileBytes = new byte[(int) data.length()];
try (FileInputStream fis = new FileInputStream(data)) {
fis.read(fileBytes);
} catch (IOException e) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.getName() + "] Failed to load extra data from " + data.getAbsolutePath(), e);
}
String jsonContent = new String(fileBytes, StandardCharsets.UTF_8);
return GsonHelper.get().fromJson(jsonContent, WorldExtraData.class);
} else {
return WorldExtraData.empty();
}
}
}
@Override
public void saveExtraData(CustomCropsWorld<World> world) {
if (VersionHelper.isVersionNewerThan1_18()) {
world.world().getPersistentDataContainer().set(WORLD_DATA, PersistentDataType.STRING,
GsonHelper.get().toJson(world.extraData()));
} else {
File data = new File(getWorldFolder(world.world()), DATA_FILE);
try (FileWriter file = new FileWriter(data)) {
GsonHelper.get().toJson(world.extraData(), file);
} catch (IOException e) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Failed to save extra data to " + data.getAbsolutePath(), e);
}
}
}
@Nullable
@Override
public CustomCropsRegion loadRegion(CustomCropsWorld<World> world, RegionPos pos, boolean createIfNotExist) {
File data = getRegionDataFile(world.world(), pos);
// if the data file not exists
if (!data.exists()) {
return createIfNotExist ? world.createRegion(pos) : null;
} else {
// load region from local files
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(data))) {
DataInputStream dataStream = new DataInputStream(bis);
CustomCropsRegion region = deserializeRegion(world, dataStream, pos);
dataStream.close();
return region;
} catch (Exception e) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Failed to load CustomCrops region data at " + pos + ". Deleting the corrupted region.", e);
boolean success = data.delete();
if (success) {
return createIfNotExist ? world.createRegion(pos) : null;
} else {
throw new RuntimeException("[" + world.worldName() + "] Failed to delete corrupted CustomCrops region data at " + pos);
}
}
}
}
@Nullable
@Override
public CustomCropsChunk loadChunk(CustomCropsWorld<World> world, ChunkPos pos, boolean createIfNotExist) {
CustomCropsRegion region = world.getOrCreateRegion(pos.toRegionPos());
// In order to reduce frequent disk reads to determine whether a region exists, we read the region into the cache
if (!region.isLoaded()) {
region.load();
}
byte[] bytes = region.getCachedChunkBytes(pos);
if (bytes == null) {
return createIfNotExist ? world.createChunk(pos) : null;
} else {
try {
long time1 = System.currentTimeMillis();
DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(bytes));
CustomCropsChunk chunk = deserializeChunk(world, dataStream);
dataStream.close();
long time2 = System.currentTimeMillis();
BukkitCustomCropsPlugin.getInstance().debug("[" + world.worldName() + "] Took " + (time2-time1) + "ms to load chunk " + pos + " from cached region");
return chunk;
} catch (IOException e) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Failed to load CustomCrops data at " + pos, e);
region.removeCachedChunk(pos);
return createIfNotExist ? world.createChunk(pos) : null;
}
}
}
@Override
public void saveRegion(CustomCropsWorld<World> world, CustomCropsRegion region) {
File file = getRegionDataFile(world.world(), region.regionPos());
long time1 = System.currentTimeMillis();
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
bos.write(serializeRegion(region));
long time2 = System.currentTimeMillis();
BukkitCustomCropsPlugin.getInstance().debug("[" + world.worldName() + "] Took " + (time2-time1) + "ms to save region " + region.regionPos());
} catch (IOException e) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Failed to save CustomCrops region data." + region.regionPos(), e);
}
}
@Override
public void saveChunk(CustomCropsWorld<World> world, CustomCropsChunk chunk) {
RegionPos pos = chunk.chunkPos().toRegionPos();
Optional<CustomCropsRegion> region = world.getLoadedRegion(pos);
if (region.isEmpty()) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("[" + world.worldName() + "] Region " + pos + " unloaded before chunk " + chunk.chunkPos() + " saving.");
} else {
CustomCropsRegion cropsRegion = region.get();
SerializableChunk serializableChunk = toSerializableChunk(chunk);
if (serializableChunk.canPrune()) {
cropsRegion.removeCachedChunk(chunk.chunkPos());
} else {
cropsRegion.setCachedChunk(chunk.chunkPos(), serializeChunk(serializableChunk));
}
}
}
@Override
public String getName(World world) {
return world.getName();
}
@Override
public long getWorldFullTime(World world) {
return world.getFullTime();
}
@Override
public int priority() {
return BUKKIT_WORLD_PRIORITY;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private CustomCropsRegion deserializeRegion(CustomCropsWorld<World> world, DataInputStream dataStream, RegionPos pos) throws IOException {
int regionVersion = dataStream.readByte();
int regionX = dataStream.readInt();
int regionZ = dataStream.readInt();
RegionPos regionPos = RegionPos.of(regionX, regionZ);
ConcurrentHashMap<ChunkPos, byte[]> map = new ConcurrentHashMap<>();
int chunkAmount = dataStream.readInt();
for (int i = 0; i < chunkAmount; i++) {
int chunkX = dataStream.readInt();
int chunkZ = dataStream.readInt();
ChunkPos chunkPos = ChunkPos.of(chunkX, chunkZ);
byte[] chunkData = new byte[dataStream.readInt()];
dataStream.read(chunkData);
map.put(chunkPos, chunkData);
}
return world.restoreRegion(pos, map);
}
private byte[] serializeRegion(CustomCropsRegion region) {
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
DataOutputStream outStream = new DataOutputStream(outByteStream);
try {
outStream.writeByte(REGION_VERSION);
outStream.writeInt(region.regionPos().x());
outStream.writeInt(region.regionPos().z());
Map<ChunkPos, byte[]> map = region.dataToSave();
outStream.writeInt(map.size());
for (Map.Entry<ChunkPos, byte[]> entry : map.entrySet()) {
outStream.writeInt(entry.getKey().x());
outStream.writeInt(entry.getKey().z());
byte[] dataArray = entry.getValue();
outStream.writeInt(dataArray.length);
outStream.write(dataArray);
}
} catch (IOException e) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to serialize CustomCrops region data." + region.regionPos(), e);
}
return outByteStream.toByteArray();
}
private CustomCropsChunk deserializeChunk(CustomCropsWorld<World> world, DataInputStream dataStream) throws IOException {
int chunkVersion = dataStream.readByte();
byte[] blockData = readCompressedBytes(dataStream);
return deserializeChunk(world, blockData, chunkVersion);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private byte[] readCompressedBytes(DataInputStream dataStream) throws IOException {
int compressedLength = dataStream.readInt();
int decompressedLength = dataStream.readInt();
byte[] compressedData = new byte[compressedLength];
byte[] decompressedData = new byte[decompressedLength];
dataStream.read(compressedData);
zstdDecompress(decompressedData, compressedData);
return decompressedData;
}
private File getWorldFolder(World world) {
return worldFolderProvider.apply(world);
}
private File getRegionDataFile(World world, RegionPos regionPos) {
return regionFileProvider.apply(world, regionPos);
}
private String getRegionDataFile(RegionPos regionPos) {
return "r." + regionPos.x() + "." + regionPos.z() + ".mcc";
}
private byte[] serializeChunk(SerializableChunk serializableChunk) {
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
DataOutputStream outStream = new DataOutputStream(outByteStream);
try {
outStream.writeByte(CHUNK_VERSION);
byte[] serializedSections = toBytes(serializableChunk);
byte[] compressed = zstdCompress(serializedSections);
outStream.writeInt(compressed.length);
outStream.writeInt(serializedSections.length);
outStream.write(compressed);
} catch (IOException e) {
BukkitCustomCropsPlugin.getInstance().getPluginLogger().severe("Failed to serialize chunk " + ChunkPos.of(serializableChunk.x(), serializableChunk.z()));
}
return outByteStream.toByteArray();
}
private byte[] toBytes(SerializableChunk chunk) throws IOException {
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384);
DataOutputStream outStream = new DataOutputStream(outByteStream);
outStream.writeInt(chunk.x());
outStream.writeInt(chunk.z());
outStream.writeInt(chunk.loadedSeconds());
outStream.writeLong(chunk.lastLoadedTime());
// write queue
int[] queue = chunk.queuedTasks();
outStream.writeInt(queue.length / 2);
for (int i : queue) {
outStream.writeInt(i);
}
// write ticked blocks
int[] tickedSet = chunk.ticked();
outStream.writeInt(tickedSet.length);
for (int i : tickedSet) {
outStream.writeInt(i);
}
// write block data
List<SerializableSection> sectionsToSave = chunk.sections();
outStream.writeInt(sectionsToSave.size());
for (SerializableSection section : sectionsToSave) {
outStream.writeInt(section.sectionID());
byte[] blockData = toBytes(section.blocks());
outStream.writeInt(blockData.length);
outStream.write(blockData);
}
return outByteStream.toByteArray();
}
private byte[] toBytes(Collection<CompoundTag> blocks) throws IOException {
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384);
DataOutputStream outStream = new DataOutputStream(outByteStream);
outStream.writeInt(blocks.size());
for (CompoundTag block : blocks) {
byte[] blockData = toBytes(block);
outStream.writeInt(blockData.length);
outStream.write(blockData);
}
return outByteStream.toByteArray();
}
private byte[] toBytes(CompoundTag tag) throws IOException {
if (tag == null || tag.getValue().isEmpty())
return new byte[0];
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
NBTOutputStream outStream = new NBTOutputStream(
outByteStream,
NBTInputStream.NO_COMPRESSION,
ByteOrder.BIG_ENDIAN
);
outStream.writeTag(tag);
return outByteStream.toByteArray();
}
@SuppressWarnings("all")
private CustomCropsChunk deserializeChunk(CustomCropsWorld world, byte[] bytes, int chunkVersion) throws IOException {
Function<String, Key> keyFunction = chunkVersion < 2 ?
(s) -> {
return Key.key("customcrops", StringUtils.toLowerCase(s));
} : s -> {
return Key.key(s);
};
DataInputStream chunkData = new DataInputStream(new ByteArrayInputStream(bytes));
// read coordinate
int x = chunkData.readInt();
int z = chunkData.readInt();
ChunkPos coordinate = new ChunkPos(x, z);
// read loading info
int loadedSeconds = chunkData.readInt();
long lastLoadedTime = chunkData.readLong();
// read task queue
int tasksSize = chunkData.readInt();
PriorityQueue<DelayedTickTask> queue = new PriorityQueue<>(Math.max(11, tasksSize));
for (int i = 0; i < tasksSize; i++) {
int time = chunkData.readInt();
BlockPos pos = new BlockPos(chunkData.readInt());
queue.add(new DelayedTickTask(time, pos));
}
// read ticked blocks
int tickedSize = chunkData.readInt();
HashSet<BlockPos> tickedSet = new HashSet<>(Math.max(11, tickedSize));
for (int i = 0; i < tickedSize; i++) {
tickedSet.add(new BlockPos(chunkData.readInt()));
}
// read block data
ConcurrentHashMap<Integer, CustomCropsSection> sectionMap = new ConcurrentHashMap<>();
int sections = chunkData.readInt();
// read sections
for (int i = 0; i < sections; i++) {
ConcurrentHashMap<BlockPos, CustomCropsBlockState> blockMap = new ConcurrentHashMap<>();
int sectionID = chunkData.readInt();
byte[] sectionBytes = new byte[chunkData.readInt()];
chunkData.read(sectionBytes);
DataInputStream sectionData = new DataInputStream(new ByteArrayInputStream(sectionBytes));
int blockAmount = sectionData.readInt();
// read blocks
for (int j = 0; j < blockAmount; j++){
byte[] blockData = new byte[sectionData.readInt()];
sectionData.read(blockData);
CompoundMap block = readCompound(blockData).getValue();
Key key = keyFunction.apply((String) block.get("type").getValue());
CompoundMap data = (CompoundMap) block.get("data").getValue();
CustomCropsBlock customBlock = Registries.BLOCK.get(key);
if (customBlock == null) {
BukkitCustomCropsPlugin.getInstance().getInstance().getPluginLogger().warn("[" + world.worldName() + "] Unrecognized custom block " + key + " has been removed from chunk " + ChunkPos.of(x, z));
continue;
}
for (int pos : (int[]) block.get("pos").getValue()) {
BlockPos blockPos = new BlockPos(pos);
blockMap.put(blockPos, CustomCropsBlockState.create(customBlock, data));
}
}
sectionMap.put(sectionID, CustomCropsSection.restore(sectionID, blockMap));
}
return world.restoreChunk(coordinate, loadedSeconds, lastLoadedTime, sectionMap, queue, tickedSet);
}
private CompoundTag readCompound(byte[] bytes) throws IOException {
if (bytes.length == 0)
return null;
NBTInputStream nbtInputStream = new NBTInputStream(
new ByteArrayInputStream(bytes),
NBTInputStream.NO_COMPRESSION,
ByteOrder.BIG_ENDIAN
);
return (CompoundTag) nbtInputStream.readTag();
}
}

View File

@@ -1,9 +1,9 @@
package net.momirealms.customcrops.mechanic.item.factory;
package net.momirealms.customcrops.bukkit.item;
import com.saicone.rtag.RtagItem;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.mechanic.item.factory.impl.ComponentItemFactory;
import net.momirealms.customcrops.mechanic.item.factory.impl.UniversalItemFactory;
import net.momirealms.customcrops.common.item.Item;
import net.momirealms.customcrops.common.item.ItemFactory;
import net.momirealms.customcrops.common.plugin.CustomCropsPlugin;
import org.bukkit.inventory.ItemStack;
import java.util.Objects;
@@ -83,4 +83,9 @@ public abstract class BukkitItemFactory extends ItemFactory<CustomCropsPlugin, R
protected ItemStack loadCopy(RtagItem item) {
return item.loadCopy();
}
@Override
protected boolean unbreakable(RtagItem item) {
return item.isUnbreakable();
}
}

View File

@@ -0,0 +1,442 @@
package net.momirealms.customcrops.bukkit.item;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.*;
import net.momirealms.customcrops.api.core.block.BreakReason;
import net.momirealms.customcrops.api.core.block.CustomCropsBlock;
import net.momirealms.customcrops.api.core.item.CustomCropsItem;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent;
import net.momirealms.customcrops.api.core.wrapper.WrappedInteractAirEvent;
import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent;
import net.momirealms.customcrops.api.core.wrapper.WrappedPlaceEvent;
import net.momirealms.customcrops.api.integration.ItemProvider;
import net.momirealms.customcrops.api.util.EventUtils;
import net.momirealms.customcrops.api.util.LocationUtils;
import net.momirealms.customcrops.api.util.PluginUtils;
import net.momirealms.customcrops.api.util.StringUtils;
import net.momirealms.customcrops.common.item.Item;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerItemDamageEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.util.*;
import static java.util.Objects.requireNonNull;
public class BukkitItemManager extends AbstractItemManager {
private final BukkitCustomCropsPlugin plugin;
private CustomItemProvider provider;
private AbstractCustomEventListener eventListener;
private final HashMap<String, ItemProvider> itemProviders = new HashMap<>();
private ItemProvider[] itemDetectArray = new ItemProvider[0];
private final BukkitItemFactory factory;
public BukkitItemManager(BukkitCustomCropsPlugin plugin) {
this.plugin = plugin;
try {
this.hookDefaultPlugins();
} catch (ReflectiveOperationException e) {
plugin.getPluginLogger().warn("Failed to load CustomItemProvider", e);
}
if (this.provider == null) {
plugin.getPluginLogger().warn("ItemsAdder/Oraxen are not installed, which can cause problems unless you use the CustomCrops API.");
}
this.factory = BukkitItemFactory.create(plugin);
}
@Override
public void setCustomEventListener(@NotNull AbstractCustomEventListener listener) {
Objects.requireNonNull(listener, "listener cannot be null");
if (this.eventListener != null) {
HandlerList.unregisterAll(this.eventListener);
}
this.eventListener = listener;
Bukkit.getPluginManager().registerEvents(this.eventListener, plugin.getBoostrap());
plugin.debug("Custom event listener set to " + listener.getClass().getName());
}
@Override
public void setCustomItemProvider(@NotNull CustomItemProvider provider) {
Objects.requireNonNull(provider, "provider cannot be null");
this.provider = provider;
plugin.debug("Custom item provider set to " + provider.getClass().getName());
}
public boolean registerItemProvider(ItemProvider item) {
if (itemProviders.containsKey(item.identifier())) return false;
itemProviders.put(item.identifier(), item);
this.resetItemDetectionOrder();
return true;
}
public boolean unregisterItemProvider(String id) {
boolean success = itemProviders.remove(id) != null;
if (success)
this.resetItemDetectionOrder();
return success;
}
private void resetItemDetectionOrder() {
ArrayList<ItemProvider> list = new ArrayList<>();
for (String plugin : ConfigManager.itemDetectOrder()) {
ItemProvider provider = itemProviders.get(plugin);
if (provider != null)
list.add(provider);
}
this.itemDetectArray = list.toArray(new ItemProvider[0]);
}
private void hookDefaultPlugins() throws ReflectiveOperationException {
if (PluginUtils.isEnabled("Oraxen")) {
String rVersion;
if (PluginUtils.getPluginVersion("Oraxen").startsWith("2")) {
rVersion = "r2";
} else {
rVersion = "r1";
}
Class<?> oraxenProviderClass = Class.forName("net.momirealms.customcrops.bukkit.integration.custom.oraxen_" + rVersion + ".OraxenProvider");
Constructor<?> oraxenProviderConstructor = oraxenProviderClass.getDeclaredConstructor();
oraxenProviderConstructor.setAccessible(true);
this.provider = (CustomItemProvider) oraxenProviderConstructor.newInstance();
Class<?> oraxenListenerClass = Class.forName("net.momirealms.customcrops.bukkit.integration.custom.oraxen_" + rVersion + ".OraxenListener");
Constructor<?> oraxenListenerConstructor = oraxenListenerClass.getDeclaredConstructor(AbstractItemManager.class);
oraxenListenerConstructor.setAccessible(true);
this.setCustomEventListener((AbstractCustomEventListener) oraxenListenerConstructor.newInstance(this));
} else if (PluginUtils.isEnabled("ItemsAdder")) {
String rVersion = "r1";
Class<?> itemsAdderProviderClass = Class.forName("net.momirealms.customcrops.bukkit.integration.custom.itemsadder_" + rVersion + ".ItemsAdderProvider");
Constructor<?> itemsAdderProviderConstructor = itemsAdderProviderClass.getDeclaredConstructor();
itemsAdderProviderConstructor.setAccessible(true);
this.provider = (CustomItemProvider) itemsAdderProviderConstructor.newInstance();
Class<?> itemsAdderListenerClass = Class.forName("net.momirealms.customcrops.bukkit.integration.custom.itemsadder_" + rVersion + ".ItemsAdderListener");
Constructor<?> itemsAdderListenerConstructor = itemsAdderListenerClass.getDeclaredConstructor(AbstractItemManager.class);
itemsAdderListenerConstructor.setAccessible(true);
this.setCustomEventListener((AbstractCustomEventListener) itemsAdderListenerConstructor.newInstance(this));
}
}
@Override
public void place(@NotNull Location location, @NotNull ExistenceForm form, @NotNull String id, FurnitureRotation rotation) {
switch (form) {
case BLOCK -> placeBlock(location, id);
case FURNITURE -> placeFurniture(location, id, rotation);
case ANY -> throw new IllegalArgumentException("Invalid existence form: " + form);
}
}
@Override
public FurnitureRotation remove(@NotNull Location location, @NotNull ExistenceForm form) {
switch (form) {
case BLOCK -> {
this.removeBlock(location);
return FurnitureRotation.NONE;
}
case FURNITURE -> {
return this.removeFurniture(location);
}
case ANY -> {
this.removeBlock(location);
return this.removeFurniture(location);
}
}
return FurnitureRotation.NONE;
}
@Override
public void placeBlock(@NotNull Location location, @NotNull String id) {
if (StringUtils.isCapitalLetter(id)) {
location.getWorld().getBlockAt(location).setType(Material.valueOf(id), false);
} else {
this.provider.placeCustomBlock(location, id);
}
}
@Override
public void placeFurniture(@NotNull Location location, @NotNull String id, FurnitureRotation rotation) {
Entity entity = this.provider.placeFurniture(location, id);
if (entity != null) {
if (entity instanceof ItemFrame itemFrame) {
itemFrame.setRotation(rotation.getBukkitRotation());
} else if (entity instanceof LivingEntity livingEntity) {
livingEntity.setRotation(rotation.getYaw(), 0);
}
}
}
@Override
public void removeBlock(@NotNull Location location) {
if (!this.provider.removeCustomBlock(location)) {
location.getBlock().setType(Material.AIR, false);
}
}
@Override
public FurnitureRotation removeFurniture(@NotNull Location location) {
Collection<Entity> entities = location.getWorld().getNearbyEntities(LocationUtils.toSurfaceCenterLocation(location), 0.5,0.25,0.5);
FurnitureRotation rotation = null;
for (Entity entity : entities) {
if (this.provider.removeFurniture(entity) && rotation == null) {
if (entity instanceof ItemFrame itemFrame) {
rotation = FurnitureRotation.getByRotation(itemFrame.getRotation());
} else {
rotation = FurnitureRotation.getByYaw(entity.getYaw());
}
}
}
return rotation;
}
@NotNull
@Override
public String blockID(@NotNull Block block) {
String id = this.provider.blockID(block);
if (id == null) {
id = block.getType().toString();
}
return id;
}
@Nullable
@Override
public String furnitureID(@NotNull Entity entity) {
return this.provider.furnitureID(entity);
}
@Override
@NotNull
public String entityID(@NotNull Entity entity) {
String id = furnitureID(entity);
if (id == null) {
id = entity.getType().toString();
}
return id;
}
@Override
@Nullable
public String furnitureID(Location location) {
Collection<Entity> entities = location.getWorld().getNearbyEntities(LocationUtils.toSurfaceCenterLocation(location), 0.5,0.25,0.5);
for (Entity entity : entities) {
if (provider.isFurniture(entity)) {
return provider.furnitureID(entity);
}
}
return null;
}
@NotNull
@Override
public String anyID(Location location) {
Block block = location.getBlock();
if (block.getType() == Material.AIR) {
String id = furnitureID(location);
if (id == null) {
return "AIR";
}
return id;
} else {
return blockID(location);
}
}
@Override
public @Nullable String id(Location location, ExistenceForm form) {
return switch (form) {
case BLOCK -> blockID(location);
case FURNITURE -> furnitureID(location);
case ANY -> anyID(location);
};
}
@NotNull
@Override
public String id(@Nullable ItemStack itemStack) {
if (itemStack == null || itemStack.getType() == Material.AIR) return "AIR";
String id = provider.itemID(itemStack);
if (id != null) return id;
for (ItemProvider p : itemDetectArray) {
id = p.itemID(itemStack);
if (id != null) return p.identifier() + ":" + id;
}
return itemStack.getType().name();
}
@Nullable
@Override
public ItemStack build(Player player, @NotNull String id) {
ItemStack itemStack = provider.itemStack(player, id);
if (itemStack != null) {
return itemStack;
}
if (!id.contains(":")) {
try {
return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH)));
} catch (IllegalArgumentException e) {
plugin.getPluginLogger().severe("Item " + id + " not exists", e);
return new ItemStack(Material.PAPER);
}
} else {
String[] split = id.split(":", 2);
ItemProvider provider = requireNonNull(itemProviders.get(split[0]), "Item provider: " + split[0] + " not found");
return requireNonNull(provider.buildItem(player, split[1]), "Item: " + split[0] + " not found");
}
}
@Override
public Item<ItemStack> wrap(ItemStack itemStack) {
return factory.wrap(itemStack);
}
@Override
public void decreaseDamage(Player player, ItemStack itemStack, int amount) {
if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0)
return;
Item<ItemStack> wrapped = factory.wrap(itemStack);
if (wrapped.unbreakable()) return;
wrapped.damage(Math.max(0, wrapped.damage().orElse(0) - amount));
wrapped.load();
}
@Override
public void increaseDamage(Player player, ItemStack itemStack, int amount) {
if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0)
return;
Item<ItemStack> wrapped = factory.wrap(itemStack);
if (wrapped.unbreakable())
return;
ItemMeta previousMeta = itemStack.getItemMeta().clone();
PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(player, itemStack, amount);
if (EventUtils.fireAndCheckCancel(itemDamageEvent)) {
plugin.debug("Another plugin modified the item from `PlayerItemDamageEvent` called by CustomCrops");
return;
}
if (!itemStack.getItemMeta().equals(previousMeta)) {
return;
}
int damage = wrapped.damage().orElse(0);
if (damage + amount >= wrapped.maxDamage().orElse((int) itemStack.getType().getMaxDurability())) {
plugin.getSenderFactory().getAudience(player).playSound(Sound.sound(Key.key("minecraft:entity.item.break"), Sound.Source.PLAYER, 1, 1));
itemStack.setAmount(0);
return;
}
wrapped.damage(damage + amount);
wrapped.load();
}
@Override
public void handlePlayerInteractAir(Player player, EquipmentSlot hand, ItemStack itemInHand) {
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
String itemID = id(itemInHand);
CustomCropsItem customCropsItem = Registries.ITEMS.get(itemID);
if (customCropsItem != null) {
customCropsItem.interactAir(new WrappedInteractAirEvent(
optionalWorld.get(),
player,
hand,
itemInHand,
itemID
));
}
}
@Override
public void handlePlayerInteractBlock(Player player, Block block, String blockID, BlockFace blockFace, EquipmentSlot hand, ItemStack itemInHand, Cancellable event) {
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
String itemID = id(itemInHand);
CustomCropsWorld<?> world = optionalWorld.get();
WrappedInteractEvent wrapped = new WrappedInteractEvent(ExistenceForm.BLOCK, player, world, block.getLocation(), blockID, itemInHand, itemID, hand, blockFace, event);
handleInteractEvent(blockID, itemID, wrapped);
}
@Override
public void handlePlayerInteractFurniture(Player player, Location location, String furnitureID, EquipmentSlot hand, ItemStack itemInHand, Cancellable event) {
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
String itemID = id(itemInHand);
CustomCropsWorld<?> world = optionalWorld.get();
WrappedInteractEvent wrapped = new WrappedInteractEvent(ExistenceForm.FURNITURE, player, world, location, furnitureID, itemInHand, itemID, hand, null, event);
handleInteractEvent(furnitureID, itemID, wrapped);
}
private void handleInteractEvent(String blockID, String itemID, WrappedInteractEvent wrapped) {
CustomCropsItem customCropsItem = Registries.ITEMS.get(itemID);
if (customCropsItem != null) {
InteractionResult result = customCropsItem.interactAt(wrapped);
if (result != InteractionResult.PASS)
return;
}
if (wrapped.isCancelled()) return;
CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(blockID);
if (customCropsBlock != null) {
customCropsBlock.onInteract(wrapped);
}
}
@Override
public void handlePlayerBreak(Player player, Location location, ItemStack itemInHand, String brokenID, Cancellable event) {
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
String itemID = id(itemInHand);
CustomCropsWorld<?> world = optionalWorld.get();
WrappedBreakEvent wrapped = new WrappedBreakEvent(player, null, world, location, brokenID, itemInHand, itemID, BreakReason.BREAK, event);
CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(brokenID);
if (customCropsBlock != null) {
customCropsBlock.onBreak(wrapped);
}
}
@Override
public void handlePlayerPlace(Player player, Location location, String placedID, EquipmentSlot hand, ItemStack itemInHand, Cancellable event) {
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(player.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
String itemID = id(itemInHand);
CustomCropsWorld<?> world = optionalWorld.get();
WrappedPlaceEvent wrapped = new WrappedPlaceEvent(player, world, location, placedID, hand, itemInHand, itemID, event);
CustomCropsBlock customCropsBlock = Registries.BLOCKS.get(placedID);
if (customCropsBlock != null) {
customCropsBlock.onPlace(wrapped);
}
}
}

View File

@@ -1,10 +1,9 @@
package net.momirealms.customcrops.mechanic.item.factory.impl;
package net.momirealms.customcrops.bukkit.item;
import com.saicone.rtag.RtagItem;
import com.saicone.rtag.data.ComponentType;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory;
import net.momirealms.customcrops.mechanic.item.factory.ComponentKeys;
import net.momirealms.customcrops.common.item.ComponentKeys;
import net.momirealms.customcrops.common.plugin.CustomCropsPlugin;
import java.util.List;
import java.util.Optional;

View File

@@ -1,10 +1,7 @@
package net.momirealms.customcrops.mechanic.item.factory.impl;
package net.momirealms.customcrops.bukkit.item;
import com.saicone.rtag.RtagItem;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.mechanic.item.factory.BukkitItemFactory;
import net.momirealms.customcrops.common.plugin.CustomCropsPlugin;
import java.util.List;
import java.util.Optional;

View File

@@ -0,0 +1,172 @@
/*
* Copyright (C) <2022> <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.bukkit.misc;
import net.kyori.adventure.text.Component;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.common.helper.AdventureHelper;
import net.momirealms.customcrops.common.helper.VersionHelper;
import net.momirealms.customcrops.common.plugin.feature.Reloadable;
import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask;
import net.momirealms.customcrops.common.util.Pair;
import net.momirealms.sparrow.heart.SparrowHeart;
import net.momirealms.sparrow.heart.feature.entity.FakeEntity;
import net.momirealms.sparrow.heart.feature.entity.armorstand.FakeArmorStand;
import net.momirealms.sparrow.heart.feature.entity.display.FakeTextDisplay;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class HologramManager implements Listener, Reloadable {
private final ConcurrentHashMap<UUID, HologramCache> hologramMap = new ConcurrentHashMap<>();
private final BukkitCustomCropsPlugin plugin;
private SchedulerTask cacheCheckTask;
private static HologramManager manager;
public static HologramManager getInstance() {
return manager;
}
public HologramManager(BukkitCustomCropsPlugin plugin) {
this.plugin = plugin;
manager = this;
}
@Override
public void load() {
Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap());
this.cacheCheckTask = plugin.getScheduler().asyncRepeating(() -> {
ArrayList<UUID> removed = new ArrayList<>();
long current = System.currentTimeMillis();
for (Map.Entry<UUID, HologramCache> entry : hologramMap.entrySet()) {
Player player = Bukkit.getPlayer(entry.getKey());
if (player == null || !player.isOnline()) {
removed.add(entry.getKey());
} else {
entry.getValue().removeOutDated(current, player);
}
}
for (UUID uuid : removed) {
hologramMap.remove(uuid);
}
}, 100, 100, TimeUnit.MILLISECONDS);
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
for (Map.Entry<UUID, HologramCache> entry : hologramMap.entrySet()) {
Player player = Bukkit.getPlayer(entry.getKey());
if (player != null && player.isOnline()) {
entry.getValue().removeAll(player);
}
}
if (cacheCheckTask != null) cacheCheckTask.cancel();
this.hologramMap.clear();
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
this.hologramMap.remove(event.getPlayer().getUniqueId());
}
public void showHologram(Player player, Location location, Component component, int millis) {
HologramCache hologramCache = hologramMap.get(player.getUniqueId());
if (hologramCache != null) {
hologramCache.showHologram(player, location, component, millis);
} else {
hologramCache = new HologramCache();
hologramCache.showHologram(player, location, component, millis);
hologramMap.put(player.getUniqueId(), hologramCache);
}
}
public static class HologramCache {
private final ConcurrentHashMap<Location, Pair<FakeEntity, Long>> cache = new ConcurrentHashMap<>();
public void removeOutDated(long current, Player player) {
ArrayList<Location> removed = new ArrayList<>();
for (Map.Entry<Location, Pair<FakeEntity, Long>> entry : cache.entrySet()) {
if (entry.getValue().right() < current) {
entry.getValue().left().destroy(player);
removed.add(entry.getKey());
}
}
for (Location location : removed) {
cache.remove(location);
}
}
public void showHologram(Player player, Location location, Component component, int millis) {
Pair<FakeEntity, Long> pair = cache.get(location);
if (pair != null) {
pair.left().destroy(player);
pair.right(System.currentTimeMillis() + millis);
if (VersionHelper.isVersionNewerThan1_19_4()) {
FakeTextDisplay fakeEntity = SparrowHeart.getInstance().createFakeTextDisplay(location.clone().add(0,1.25,0));
fakeEntity.name(AdventureHelper.componentToJson(component));
fakeEntity.rgba(0, 0, 0, 0);
fakeEntity.spawn(player);
pair.left(fakeEntity);
} else {
FakeArmorStand fakeEntity = SparrowHeart.getInstance().createFakeArmorStand(location);
fakeEntity.name(AdventureHelper.componentToJson(component));
fakeEntity.small(true);
fakeEntity.invisible(true);
fakeEntity.spawn(player);
pair.left(fakeEntity);
}
} else {
long removeTime = System.currentTimeMillis() + millis;
if (VersionHelper.isVersionNewerThan1_19_4()) {
FakeTextDisplay fakeEntity = SparrowHeart.getInstance().createFakeTextDisplay(location.clone().add(0,1.25,0));
fakeEntity.name(AdventureHelper.componentToJson(component));
fakeEntity.rgba(0, 0, 0, 0);
fakeEntity.spawn(player);
this.cache.put(location, Pair.of(fakeEntity, removeTime));
} else {
FakeArmorStand fakeEntity = SparrowHeart.getInstance().createFakeArmorStand(location);
fakeEntity.name(AdventureHelper.componentToJson(component));
fakeEntity.small(true);
fakeEntity.invisible(true);
fakeEntity.spawn(player);
this.cache.put(location, Pair.of(fakeEntity, removeTime));
}
}
}
public void removeAll(Player player) {
for (Map.Entry<Location, Pair<FakeEntity, Long>> entry : this.cache.entrySet()) {
entry.getValue().left().destroy(player);
}
cache.clear();
}
}
}

View File

@@ -0,0 +1,52 @@
package net.momirealms.customcrops.bukkit.requirement;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.core.block.CropBlock;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.requirement.AbstractRequirementManager;
public class BlockRequirementManager extends AbstractRequirementManager<CustomCropsBlockState> {
public BlockRequirementManager(BukkitCustomCropsPlugin plugin) {
super(plugin, CustomCropsBlockState.class);
}
@Override
protected void registerBuiltInRequirements() {
super.registerBuiltInRequirements();
this.registerPointCondition();
}
@Override
public void load() {
loadExpansions(CustomCropsBlockState.class);
}
private void registerPointCondition() {
registerRequirement((args, actions, runActions) -> {
int value = (int) args;
return (context) -> {
CustomCropsBlockState state = context.holder();
if (state.type() instanceof CropBlock cropBlock) {
int point = cropBlock.point(state);
if (point > value) return true;
}
if (runActions) ActionManager.trigger(context, actions);
return false;
};
}, "point_more_than", "point-more-than");
registerRequirement((args, actions, runActions) -> {
int value = (int) args;
return (context) -> {
CustomCropsBlockState state = context.holder();
if (state.type() instanceof CropBlock cropBlock) {
int point = cropBlock.point(state);
if (point < value) return true;
}
if (runActions) ActionManager.trigger(context, actions);
return false;
};
}, "point_less_than", "point-less-than");
}
}

View File

@@ -0,0 +1,247 @@
package net.momirealms.customcrops.bukkit.requirement;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.action.ActionManager;
import net.momirealms.customcrops.api.integration.LevelerProvider;
import net.momirealms.customcrops.api.misc.value.MathValue;
import net.momirealms.customcrops.api.requirement.AbstractRequirementManager;
import net.momirealms.customcrops.api.requirement.Requirement;
import net.momirealms.customcrops.bukkit.integration.VaultHook;
import net.momirealms.customcrops.common.util.ListUtils;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.util.List;
import java.util.Locale;
public class PlayerRequirementManager extends AbstractRequirementManager<Player> {
public PlayerRequirementManager(BukkitCustomCropsPlugin plugin) {
super(plugin, Player.class);
}
@Override
protected void registerBuiltInRequirements() {
super.registerBuiltInRequirements();
this.registerItemInHandRequirement();
this.registerPermissionRequirement();
this.registerPluginLevelRequirement();
this.registerCoolDownRequirement();
this.registerLevelRequirement();
this.registerMoneyRequirement();
this.registerPotionEffectRequirement();
this.registerSneakRequirement();
this.registerGameModeRequirement();
}
@Override
public void load() {
loadExpansions(Player.class);
}
private void registerItemInHandRequirement() {
registerRequirement((args, actions, runActions) -> {
if (args instanceof Section section) {
boolean mainOrOff = section.getString("hand","main").equalsIgnoreCase("main");
int amount = section.getInt("amount", 1);
List<String> items = ListUtils.toList(section.get("item"));
return context -> {
if (context.holder() == null) return true;
ItemStack itemStack = mainOrOff ?
context.holder().getInventory().getItemInMainHand()
: context.holder().getInventory().getItemInOffHand();
String id = plugin.getItemManager().id(itemStack);
if (items.contains(id) && itemStack.getAmount() >= amount) return true;
if (runActions) ActionManager.trigger(context, actions);
return false;
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at item-in-hand requirement which is expected be `Section`");
return Requirement.empty();
}
}, "item-in-hand");
}
private void registerPluginLevelRequirement() {
registerRequirement((args, actions, runActions) -> {
if (args instanceof Section section) {
String pluginName = section.getString("plugin");
int level = section.getInt("level");
String target = section.getString("target");
return context -> {
if (context.holder() == null) return true;
LevelerProvider levelerProvider = plugin.getIntegrationManager().getLevelerProvider(pluginName);
if (levelerProvider == null) {
plugin.getPluginLogger().warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation.");
return true;
}
if (levelerProvider.getLevel(context.holder(), target) >= level)
return true;
if (runActions) ActionManager.trigger(context, actions);
return false;
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plugin-level requirement which is expected be `Section`");
return Requirement.empty();
}
}, "plugin-level");
}
private void registerLevelRequirement() {
registerRequirement((args, actions, runActions) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return true;
int current = context.holder().getLevel();
if (current >= value.evaluate(context, true))
return true;
if (runActions) ActionManager.trigger(context, actions);
return false;
};
}, "level");
}
private void registerMoneyRequirement() {
registerRequirement((args, actions, runActions) -> {
MathValue<Player> value = MathValue.auto(args);
return context -> {
if (context.holder() == null) return true;
double current = VaultHook.getBalance(context.holder());
if (current >= value.evaluate(context, true))
return true;
if (runActions) ActionManager.trigger(context, actions);
return false;
};
}, "money");
}
private void registerCoolDownRequirement() {
registerRequirement((args, actions, runActions) -> {
if (args instanceof Section section) {
String key = section.getString("key");
int time = section.getInt("time");
return context -> {
if (context.holder() == null) return true;
if (!plugin.getCoolDownManager().isCoolDown(context.holder().getUniqueId(), key, time))
return true;
if (runActions) ActionManager.trigger(context, actions);
return false;
};
} else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at cooldown requirement which is expected be `Section`");
return Requirement.empty();
}
}, "cooldown");
}
private void registerPermissionRequirement() {
registerRequirement((args, actions, runActions) -> {
List<String> perms = ListUtils.toList(args);
return context -> {
if (context.holder() == null) return true;
for (String perm : perms)
if (context.holder().hasPermission(perm))
return true;
if (runActions) ActionManager.trigger(context, actions);
return false;
};
}, "permission");
registerRequirement((args, actions, runActions) -> {
List<String> perms = ListUtils.toList(args);
return context -> {
if (context.holder() == null) return true;
for (String perm : perms)
if (context.holder().hasPermission(perm)) {
if (runActions) ActionManager.trigger(context, actions);
return false;
}
return true;
};
}, "!permission");
}
@SuppressWarnings("deprecation")
private void registerPotionEffectRequirement() {
registerRequirement((args, actions, runActions) -> {
String potions = (String) args;
String[] split = potions.split("(<=|>=|<|>|==)", 2);
PotionEffectType type = PotionEffectType.getByName(split[0]);
if (type == null) {
plugin.getPluginLogger().warn("Potion effect doesn't exist: " + split[0]);
return Requirement.empty();
}
int required = Integer.parseInt(split[1]);
String operator = potions.substring(split[0].length(), potions.length() - split[1].length());
return context -> {
if (context.holder() == null) return true;
int level = -1;
PotionEffect potionEffect = context.holder().getPotionEffect(type);
if (potionEffect != null) {
level = potionEffect.getAmplifier();
}
boolean result = false;
switch (operator) {
case ">=" -> {
if (level >= required) result = true;
}
case ">" -> {
if (level > required) result = true;
}
case "==" -> {
if (level == required) result = true;
}
case "!=" -> {
if (level != required) result = true;
}
case "<=" -> {
if (level <= required) result = true;
}
case "<" -> {
if (level < required) result = true;
}
}
if (result) {
return true;
}
if (runActions) ActionManager.trigger(context, actions);
return false;
};
}, "potion-effect");
}
private void registerSneakRequirement() {
registerRequirement((args, actions, advanced) -> {
boolean sneak = (boolean) args;
return context -> {
if (context.holder() == null) return true;
if (sneak) {
if (context.holder().isSneaking())
return true;
} else {
if (!context.holder().isSneaking())
return true;
}
if (advanced) ActionManager.trigger(context, actions);
return false;
};
}, "sneak");
}
protected void registerGameModeRequirement() {
registerRequirement((args, actions, advanced) -> {
List<String> modes = ListUtils.toList(args);
return context -> {
if (context.holder() == null) return true;
var name = context.holder().getGameMode().name().toLowerCase(Locale.ENGLISH);
if (modes.contains(name)) {
return true;
}
if (advanced) ActionManager.trigger(context, actions);
return false;
};
}, "gamemode");
}
}

View File

@@ -23,30 +23,31 @@
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.classpath;
package net.momirealms.customcrops.bukkit.scheduler;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.nio.file.Path;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.bukkit.scheduler.impl.BukkitExecutor;
import net.momirealms.customcrops.bukkit.scheduler.impl.FoliaExecutor;
import net.momirealms.customcrops.common.helper.VersionHelper;
import net.momirealms.customcrops.common.plugin.scheduler.AbstractJavaScheduler;
import net.momirealms.customcrops.common.plugin.scheduler.RegionExecutor;
import org.bukkit.Location;
import org.bukkit.World;
public class ReflectionClassPathAppender implements ClassPathAppender {
private final URLClassLoaderAccess classLoaderAccess;
public class BukkitSchedulerAdapter extends AbstractJavaScheduler<Location, World> {
protected RegionExecutor<Location, World> sync;
public ReflectionClassPathAppender(ClassLoader classLoader) throws IllegalStateException {
if (classLoader instanceof URLClassLoader) {
this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader);
public BukkitSchedulerAdapter(BukkitCustomCropsPlugin plugin) {
super(plugin);
if (VersionHelper.isFolia()) {
this.sync = new FoliaExecutor(plugin.getBoostrap());
} else {
throw new IllegalStateException("ClassLoader is not instance of URLClassLoader");
this.sync = new BukkitExecutor(plugin.getBoostrap());
}
}
@Override
public void addJarToClasspath(Path file) {
try {
this.classLoaderAccess.addURL(file.toUri().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
public RegionExecutor<Location, World> sync() {
return this.sync;
}
}

View File

@@ -0,0 +1,15 @@
package net.momirealms.customcrops.bukkit.scheduler;
import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask;
public class DummyTask implements SchedulerTask {
@Override
public void cancel() {
}
@Override
public boolean isCancelled() {
return true;
}
}

View File

@@ -0,0 +1,95 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.bukkit.scheduler.impl;
import net.momirealms.customcrops.bukkit.scheduler.DummyTask;
import net.momirealms.customcrops.common.plugin.scheduler.RegionExecutor;
import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
public class BukkitExecutor implements RegionExecutor<Location, World> {
private final Plugin plugin;
public BukkitExecutor(Plugin plugin) {
this.plugin = plugin;
}
@Override
public void run(Runnable r, Location l) {
if (Bukkit.isPrimaryThread()) {
r.run();
} else {
Bukkit.getScheduler().runTask(plugin, r);
}
}
@Override
public void run(Runnable r, World world, int x, int z) {
run(r);
}
@Override
public SchedulerTask runLater(Runnable r, long delayTicks, Location l) {
if (delayTicks == 0) {
if (Bukkit.isPrimaryThread()) {
r.run();
return new DummyTask();
} else {
return new BukkitCancellable(Bukkit.getScheduler().runTask(plugin, r));
}
}
return new BukkitCancellable(Bukkit.getScheduler().runTaskLater(plugin, r, delayTicks));
}
@Override
public SchedulerTask runRepeating(Runnable r, long delayTicks, long period, Location l) {
return new BukkitCancellable(Bukkit.getScheduler().runTaskTimer(plugin, r, delayTicks, period));
}
public static class BukkitCancellable implements SchedulerTask {
private final BukkitTask task;
public BukkitCancellable(BukkitTask task) {
this.task = task;
}
@Override
public void cancel() {
this.task.cancel();
}
@Override
public boolean isCancelled() {
return this.task.isCancelled();
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.bukkit.scheduler.impl;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import net.momirealms.customcrops.common.plugin.scheduler.RegionExecutor;
import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import java.util.Optional;
public class FoliaExecutor implements RegionExecutor<Location, World> {
private final Plugin plugin;
public FoliaExecutor(Plugin plugin) {
this.plugin = plugin;
}
@Override
public void run(Runnable r, Location l) {
Optional.ofNullable(l).ifPresentOrElse(loc -> Bukkit.getRegionScheduler().execute(plugin, loc, r), () -> Bukkit.getGlobalRegionScheduler().execute(plugin, r));
}
@Override
public void run(Runnable r, World world, int x, int z) {
Bukkit.getRegionScheduler().execute(plugin, world, x, z, r);
}
@Override
public SchedulerTask runLater(Runnable r, long delayTicks, Location l) {
if (l == null) {
if (delayTicks == 0) {
return new FoliaCancellable(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> r.run(), delayTicks));
} else {
return new FoliaCancellable(Bukkit.getGlobalRegionScheduler().run(plugin, scheduledTask -> r.run()));
}
} else {
if (delayTicks == 0) {
return new FoliaCancellable(Bukkit.getRegionScheduler().run(plugin, l, scheduledTask -> r.run()));
} else {
return new FoliaCancellable(Bukkit.getRegionScheduler().runDelayed(plugin, l, scheduledTask -> r.run(), delayTicks));
}
}
}
@Override
public SchedulerTask runRepeating(Runnable r, long delayTicks, long period, Location l) {
if (l == null) {
return new FoliaCancellable(Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, scheduledTask -> r.run(), delayTicks, period));
} else {
return new FoliaCancellable(Bukkit.getRegionScheduler().runAtFixedRate(plugin, l, scheduledTask -> r.run(), delayTicks, period));
}
}
public static class FoliaCancellable implements SchedulerTask {
private final ScheduledTask task;
public FoliaCancellable(ScheduledTask task) {
this.task = task;
}
@Override
public void cancel() {
this.task.cancel();
}
@Override
public boolean isCancelled() {
return this.task.isCancelled();
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.bukkit.sender;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.common.sender.Sender;
import net.momirealms.customcrops.common.sender.SenderFactory;
import net.momirealms.customcrops.common.util.Tristate;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.RemoteConsoleCommandSender;
import org.bukkit.entity.Player;
import java.util.UUID;
public class BukkitSenderFactory extends SenderFactory<BukkitCustomCropsPlugin, CommandSender> {
private final BukkitAudiences audiences;
public BukkitSenderFactory(BukkitCustomCropsPlugin plugin) {
super(plugin);
this.audiences = BukkitAudiences.create(plugin.getBoostrap());
}
@Override
protected String getName(CommandSender sender) {
if (sender instanceof Player) {
return sender.getName();
}
return Sender.CONSOLE_NAME;
}
@Override
protected UUID getUniqueId(CommandSender sender) {
if (sender instanceof Player) {
return ((Player) sender).getUniqueId();
}
return Sender.CONSOLE_UUID;
}
@Override
public Audience getAudience(CommandSender sender) {
return this.audiences.sender(sender);
}
@Override
protected void sendMessage(CommandSender sender, Component message) {
// we can safely send async for players and the console - otherwise, send it sync
if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
getAudience(sender).sendMessage(message);
} else {
getPlugin().getScheduler().executeSync(() -> getAudience(sender).sendMessage(message));
}
}
@Override
protected Tristate getPermissionValue(CommandSender sender, String node) {
if (sender.hasPermission(node)) {
return Tristate.TRUE;
} else if (sender.isPermissionSet(node)) {
return Tristate.FALSE;
} else {
return Tristate.UNDEFINED;
}
}
@Override
protected boolean hasPermission(CommandSender sender, String node) {
return sender.hasPermission(node);
}
@Override
protected void performCommand(CommandSender sender, String command) {
getPlugin().getBoostrap().getServer().dispatchCommand(sender, command);
}
@Override
protected boolean isConsole(CommandSender sender) {
return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender;
}
@Override
public void close() {
super.close();
this.audiences.close();
}
}

View File

@@ -0,0 +1,327 @@
package net.momirealms.customcrops.bukkit.world;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.ConfigManager;
import net.momirealms.customcrops.api.core.world.*;
import net.momirealms.customcrops.api.core.world.adaptor.WorldAdaptor;
import net.momirealms.customcrops.api.integration.SeasonProvider;
import net.momirealms.customcrops.bukkit.config.BukkitConfigManager;
import net.momirealms.customcrops.bukkit.integration.adaptor.BukkitWorldAdaptor;
import net.momirealms.customcrops.bukkit.integration.adaptor.asp_r1.SlimeWorldAdaptorR1;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.world.*;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitWorldManager implements WorldManager, Listener {
private final BukkitCustomCropsPlugin plugin;
private final Set<WorldAdaptor<?>> adaptors = new TreeSet<>();
private final ConcurrentHashMap<String, CustomCropsWorld<?>> worlds = new ConcurrentHashMap<>();
private final HashMap<String, WorldSetting> worldSettings = new HashMap<>();
private WorldSetting defaultWorldSetting;
private MatchRule matchRule;
private HashSet<String> worldList;
private SeasonProvider seasonProvider;
public BukkitWorldManager(BukkitCustomCropsPlugin plugin) {
this.plugin = plugin;
try {
Class.forName("com.infernalsuite.aswm.api.SlimePlugin");
adaptors.add(new SlimeWorldAdaptorR1(1));
} catch (ClassNotFoundException ignored) {
}
if (Bukkit.getPluginManager().isPluginEnabled("SlimeWorldPlugin")) {
adaptors.add(new SlimeWorldAdaptorR1(2));
}
this.adaptors.add(new BukkitWorldAdaptor());
this.seasonProvider = new SeasonProvider() {
@NotNull
@Override
public Season getSeason(@NotNull World world) {
return BukkitWorldManager.this.getWorld(world).map(w -> w.extraData().getSeason()).orElse(Season.DISABLE);
}
@Override
public String identifier() {
return "CustomCrops";
}
};
}
public void seasonProvider(SeasonProvider seasonProvider) {
this.seasonProvider = seasonProvider;
}
@Override
public Season getSeason(World world) {
if (ConfigManager.syncSeasons()) {
World reference = Bukkit.getWorld(ConfigManager.referenceWorld());
if (reference != null) {
return seasonProvider.getSeason(reference);
} else {
return Season.DISABLE;
}
} else {
return seasonProvider.getSeason(world);
}
}
@Override
public int getDate(World world) {
if (ConfigManager.syncSeasons()) {
World reference = Bukkit.getWorld(ConfigManager.referenceWorld());
if (reference != null) {
return getWorld(reference).map(w -> w.extraData().getDate()).orElse(-1);
} else {
return -1;
}
} else {
return getWorld(world).map(w -> w.extraData().getDate()).orElse(-1);
}
}
@Override
public void load() {
this.loadConfig();
Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap());
// load and unload worlds
for (World world : Bukkit.getWorlds()) {
if (isMechanicEnabled(world)) {
loadWorld(world);
} else {
unloadWorld(world);
}
}
}
private void loadConfig() {
YamlDocument config = BukkitConfigManager.getMainConfig();
Section section = config.getSection("worlds");
if (section == null) {
plugin.getPluginLogger().warn("worlds section should not be null");
return;
}
this.matchRule = MatchRule.valueOf(section.getString("mode", "blacklist").toUpperCase(Locale.ENGLISH));
this.worldList = new HashSet<>(section.getStringList("list"));
Section settingSection = section.getSection("settings");
if (settingSection == null) {
plugin.getPluginLogger().warn("worlds.settings section should not be null");
return;
}
Section defaultSection = settingSection.getSection("_DEFAULT_");
if (defaultSection == null) {
plugin.getPluginLogger().warn("worlds.settings._DEFAULT_ section should not be null");
return;
}
this.defaultWorldSetting = sectionToWorldSetting(defaultSection);
Section worldSection = settingSection.getSection("_WORLDS_");
if (worldSection != null) {
for (Map.Entry<String, Object> entry : worldSection.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section inner) {
this.worldSettings.put(entry.getKey(), sectionToWorldSetting(inner));
}
}
}
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
this.worldSettings.clear();
}
@Override
public void disable() {
this.unload();
for (World world : Bukkit.getWorlds()) {
unloadWorld(world);
}
}
@Override
public CustomCropsWorld<?> loadWorld(World world) {
Optional<CustomCropsWorld<?>> optionalWorld = getWorld(world);
if (optionalWorld.isPresent()) {
CustomCropsWorld<?> customCropsWorld = optionalWorld.get();
customCropsWorld.setting(Optional.ofNullable(worldSettings.get(world.getName())).orElse(defaultWorldSetting));
return customCropsWorld;
}
CustomCropsWorld<?> adaptedWorld = adapt(world);
adaptedWorld.setting(Optional.ofNullable(worldSettings.get(world.getName())).orElse(defaultWorldSetting));
adaptedWorld.setTicking(true);
this.worlds.put(world.getName(), adaptedWorld);
for (Chunk chunk : world.getLoadedChunks()) {
loadLoadedChunk(adaptedWorld, ChunkPos.fromBukkitChunk(chunk));
}
return adaptedWorld;
}
// Before using the method, make sure that the bukkit chunk is loaded
public void loadLoadedChunk(CustomCropsWorld<?> world, ChunkPos pos) {
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));
}
@Override
public boolean unloadWorld(World world) {
CustomCropsWorld<?> removedWorld = worlds.remove(world.getName());
if (removedWorld == null) {
return false;
}
removedWorld.setTicking(false);
removedWorld.save();
return true;
}
@EventHandler
public void onWorldSave(WorldSaveEvent event) {
final World world = event.getWorld();
getWorld(world).ifPresent(CustomCropsWorld::save);
}
@EventHandler (priority = EventPriority.HIGH)
public void onWorldLoad(WorldLoadEvent event) {
World world = event.getWorld();
if (!isMechanicEnabled(world)) return;
loadWorld(world);
}
@EventHandler (priority = EventPriority.LOW)
public void onWorldUnload(WorldUnloadEvent event) {
World world = event.getWorld();
if (!isMechanicEnabled(world)) return;
unloadWorld(world);
}
@EventHandler
public void onChunkUnload(ChunkUnloadEvent event) {
final Chunk chunk = event.getChunk();
final World world = event.getWorld();
this.getWorld(world)
.flatMap(customWorld -> customWorld.getLoadedChunk(ChunkPos.fromBukkitChunk(chunk)))
.ifPresent(customChunk -> customChunk.unload(true));
}
@EventHandler
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)));
}
@EventHandler
public void onEntitiesLoad(EntitiesLoadEvent event) {
}
public boolean isMechanicEnabled(World world) {
if (matchRule == MatchRule.WHITELIST) {
return worldList.contains(world.getName());
} else if (matchRule == MatchRule.BLACKLIST) {
return !worldList.contains(world.getName());
} else {
for (String regex : worldList) {
if (world.getName().matches(regex)) {
return true;
}
}
return false;
}
}
@Override
public Optional<CustomCropsWorld<?>> getWorld(World world) {
return getWorld(world.getName());
}
@Override
public Optional<CustomCropsWorld<?>> getWorld(String world) {
return Optional.ofNullable(worlds.get(world));
}
@Override
public boolean isWorldLoaded(World world) {
return worlds.containsKey(world.getName());
}
@Override
public Set<WorldAdaptor<?>> adaptors() {
return adaptors;
}
@Override
public CustomCropsWorld<?> adapt(World world) {
return adapt(world.getName());
}
@Override
public CustomCropsWorld<?> adapt(String name) {
for (WorldAdaptor<?> adaptor : adaptors) {
Object world = adaptor.getWorld(name);
if (world != null) {
return adaptor.adapt(world);
}
}
throw new RuntimeException("Unable to adapt world " + name);
}
public enum MatchRule {
BLACKLIST,
WHITELIST,
REGEX
}
private static WorldSetting sectionToWorldSetting(Section section) {
return WorldSetting.of(
section.getBoolean("enable", true),
section.getInt("min-tick-unit", 300),
getRandomTickModeByString(section.getString("crop.mode")),
section.getInt("crop.tick-interval", 1),
getRandomTickModeByString(section.getString("pot.mode")),
section.getInt("pot.tick-interval", 2),
getRandomTickModeByString(section.getString("sprinkler.mode")),
section.getInt("sprinkler.tick-interval", 2),
section.getBoolean("offline-tick.enable", false),
section.getInt("offline-tick.max-offline-seconds", 1200),
section.getBoolean("season.enable", false),
section.getBoolean("season.auto-alternation", false),
section.getInt("season.duration", 28),
section.getInt("crop.max-per-chunk", 128),
section.getInt("pot.max-per-chunk", -1),
section.getInt("sprinkler.max-per-chunk", 32),
section.getInt("random-tick-speed", 0)
);
}
private static boolean getRandomTickModeByString(String str) {
if (str == null) {
return false;
}
if (str.equalsIgnoreCase("RANDOM_TICK")) {
return true;
}
if (str.equalsIgnoreCase("SCHEDULED_TICK")) {
return false;
}
throw new IllegalArgumentException("Invalid mode found when loading world settings: " + str);
}
}

View File

@@ -1,153 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.integration.LevelInterface;
import net.momirealms.customcrops.api.integration.SeasonInterface;
import net.momirealms.customcrops.api.manager.IntegrationManager;
import net.momirealms.customcrops.api.util.LogUtils;
import net.momirealms.customcrops.compatibility.item.MMOItemsItemImpl;
import net.momirealms.customcrops.compatibility.item.MythicMobsItemImpl;
import net.momirealms.customcrops.compatibility.item.NeigeItemsItemImpl;
import net.momirealms.customcrops.compatibility.item.ZaphkielItemImpl;
import net.momirealms.customcrops.compatibility.level.*;
import net.momirealms.customcrops.compatibility.quest.BattlePassHook;
import net.momirealms.customcrops.compatibility.quest.BetonQuestHook;
import net.momirealms.customcrops.compatibility.quest.ClueScrollsHook;
import net.momirealms.customcrops.compatibility.season.AdvancedSeasonsImpl;
import net.momirealms.customcrops.compatibility.season.InBuiltSeason;
import net.momirealms.customcrops.compatibility.season.RealisticSeasonsImpl;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
public class IntegrationManagerImpl implements IntegrationManager {
private final CustomCropsPlugin plugin;
private final HashMap<String, LevelInterface> levelPluginMap;
private SeasonInterface seasonInterface;
public IntegrationManagerImpl(CustomCropsPlugin plugin) {
this.plugin = plugin;
this.levelPluginMap = new HashMap<>();
}
@Override
public void init() {
if (plugin.doesHookedPluginExist("MMOItems")) {
plugin.getItemManager().registerItemLibrary(new MMOItemsItemImpl());
hookMessage("MMOItems");
}
if (plugin.doesHookedPluginExist("Zaphkiel")) {
plugin.getItemManager().registerItemLibrary(new ZaphkielItemImpl());
hookMessage("Zaphkiel");
}
if (plugin.doesHookedPluginExist("NeigeItems")) {
plugin.getItemManager().registerItemLibrary(new NeigeItemsItemImpl());
hookMessage("NeigeItems");
}
if (plugin.doesHookedPluginExist("MythicMobs")) {
plugin.getItemManager().registerItemLibrary(new MythicMobsItemImpl());
hookMessage("MythicMobs");
}
if (plugin.doesHookedPluginExist("EcoJobs")) {
registerLevelPlugin("EcoJobs", new EcoJobsImpl());
hookMessage("EcoJobs");
}
if (plugin.doesHookedPluginExist("Jobs")) {
registerLevelPlugin("JobsReborn", new JobsRebornImpl());
hookMessage("JobsReborn");
}
if (plugin.doesHookedPluginExist("AureliumSkills")) {
registerLevelPlugin("AureliumSkills", new AureliumSkillsImpl());
hookMessage("AureliumSkills");
}
if (plugin.doesHookedPluginExist("EcoSkills")) {
registerLevelPlugin("EcoSkills", new EcoSkillsImpl());
hookMessage("EcoSkills");
}
if (plugin.doesHookedPluginExist("mcMMO")) {
registerLevelPlugin("mcMMO", new McMMOImpl());
hookMessage("mcMMO");
}
if (plugin.doesHookedPluginExist("MMOCore")) {
registerLevelPlugin("MMOCore", new MMOCoreImpl());
hookMessage("MMOCore");
}
if (plugin.doesHookedPluginExist("AuraSkills")) {
registerLevelPlugin("AuraSkills", new AuraSkillsImpl());
hookMessage("AuraSkills");
}
if (plugin.isHookedPluginEnabled("BattlePass")){
BattlePassHook battlePassHook = new BattlePassHook();
battlePassHook.register();
hookMessage("BattlePass");
}
if (plugin.isHookedPluginEnabled("ClueScrolls")) {
ClueScrollsHook clueScrollsHook = new ClueScrollsHook();
clueScrollsHook.register();
hookMessage("ClueScrolls");
}
if (plugin.isHookedPluginEnabled("BetonQuest")) {
BetonQuestHook.register();
hookMessage("BetonQuest");
}
if (plugin.isHookedPluginEnabled("RealisticSeasons")) {
this.seasonInterface = new RealisticSeasonsImpl();
hookMessage("RealisticSeasons");
} else if (plugin.isHookedPluginEnabled("AdvancedSeasons", "1.4", "1.5")) {
this.seasonInterface = new AdvancedSeasonsImpl();
hookMessage("AdvancedSeasons");
} else {
this.seasonInterface = new InBuiltSeason(plugin.getWorldManager());
}
}
@Override
public void disable() {
}
@Override
public boolean registerLevelPlugin(String plugin, LevelInterface level) {
if (levelPluginMap.containsKey(plugin)) return false;
levelPluginMap.put(plugin, level);
return true;
}
@Override
public boolean unregisterLevelPlugin(String plugin) {
return levelPluginMap.remove(plugin) != null;
}
private void hookMessage(String plugin) {
LogUtils.info( plugin + " hooked!");
}
@Override
@Nullable
public LevelInterface getLevelPlugin(String plugin) {
return levelPluginMap.get(plugin);
}
@Override
public SeasonInterface getSeasonInterface() {
return seasonInterface;
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility;
import net.milkbowl.vault.economy.Economy;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import org.bukkit.plugin.RegisteredServiceProvider;
public class VaultHook {
private static Economy economy;
public static boolean initialize() {
RegisteredServiceProvider<Economy> rsp = CustomCropsPlugin.getInstance().getServer().getServicesManager().getRegistration(Economy.class);
if (rsp == null) {
return false;
}
economy = rsp.getProvider();
return true;
}
public static Economy getEconomy() {
return economy;
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.item;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.momirealms.customcrops.api.integration.ItemLibrary;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Locale;
public class MMOItemsItemImpl implements ItemLibrary {
@Override
public String identification() {
return "MMOItems";
}
@Override
public ItemStack buildItem(Player player, String id) {
String[] split = id.split(":");
MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ENGLISH));
return mmoItem == null ? new ItemStack(Material.AIR) : mmoItem.newBuilder().build();
}
@Override
public String getItemID(ItemStack itemStack) {
NBTItem nbtItem = NBTItem.get(itemStack);
if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null;
return nbtItem.getString("MMOITEMS_ITEM_TYPE") + ":" + nbtItem.getString("MMOITEMS_ITEM_ID");
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.item;
import io.lumine.mythic.bukkit.MythicBukkit;
import net.momirealms.customcrops.api.integration.ItemLibrary;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class MythicMobsItemImpl implements ItemLibrary {
private MythicBukkit mythicBukkit;
public MythicMobsItemImpl() {
this.mythicBukkit = MythicBukkit.inst();
}
@Override
public String identification() {
return "MythicMobs";
}
@Override
public ItemStack buildItem(Player player, String id) {
if (mythicBukkit == null || mythicBukkit.isClosed()) {
this.mythicBukkit = MythicBukkit.inst();
}
return mythicBukkit.getItemManager().getItemStack(id);
}
@Override
public String getItemID(ItemStack itemStack) {
return mythicBukkit.getItemManager().getMythicTypeFromItem(itemStack);
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.item;
import net.momirealms.customcrops.api.integration.ItemLibrary;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import pers.neige.neigeitems.item.ItemInfo;
import pers.neige.neigeitems.manager.ItemManager;
import pers.neige.neigeitems.utils.ItemUtils;
public class NeigeItemsItemImpl implements ItemLibrary {
@Override
public String identification() {
return "NeigeItems";
}
@Override
public ItemStack buildItem(Player player, String id) {
return ItemManager.INSTANCE.getItemStack(id, player);
}
@Override
public String getItemID(ItemStack itemStack) {
ItemInfo itemInfo = ItemUtils.isNiItem(itemStack);
if (itemInfo != null) {
return itemInfo.getId();
}
return null;
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.item;
import ink.ptms.zaphkiel.ZapAPI;
import ink.ptms.zaphkiel.Zaphkiel;
import net.momirealms.customcrops.api.integration.ItemLibrary;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class ZaphkielItemImpl implements ItemLibrary {
private final ZapAPI zapAPI;
public ZaphkielItemImpl() {
this.zapAPI = Zaphkiel.INSTANCE.api();
}
@Override
public String identification() {
return "Zaphkiel";
}
@Override
public ItemStack buildItem(Player player, String id) {
return zapAPI.getItemManager().generateItemStack(id, player);
}
@Override
public String getItemID(ItemStack itemStack) {
if (itemStack == null || itemStack.getType() == Material.AIR) return null;
return zapAPI.getItemHandler().getItemId(itemStack);
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.level;
import dev.aurelium.auraskills.api.AuraSkillsApi;
import dev.aurelium.auraskills.api.registry.NamespacedId;
import net.momirealms.customcrops.api.integration.LevelInterface;
import org.bukkit.entity.Player;
public class AuraSkillsImpl implements LevelInterface {
@Override
public void addXp(Player player, String target, double amount) {
AuraSkillsApi.get().getUser(player.getUniqueId())
.addSkillXp(AuraSkillsApi.get().getGlobalRegistry().getSkill(NamespacedId.fromDefault(target)), amount);
}
@Override
public int getLevel(Player player, String target) {
return AuraSkillsApi.get().getUser(player.getUniqueId()).getSkillLevel(
AuraSkillsApi.get().getGlobalRegistry().getSkill(NamespacedId.fromDefault(target))
);
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.level;
import com.archyx.aureliumskills.api.AureliumAPI;
import net.momirealms.customcrops.api.integration.LevelInterface;
import org.bukkit.entity.Player;
public class AureliumSkillsImpl implements LevelInterface {
@Override
public void addXp(Player player, String target, double amount) {
AureliumAPI.getPlugin().getLeveler().addXp(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target), amount);
}
@Override
public int getLevel(Player player, String target) {
return AureliumAPI.getSkillLevel(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target));
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.level;
import com.willfp.ecojobs.api.EcoJobsAPI;
import com.willfp.ecojobs.jobs.Job;
import com.willfp.ecojobs.jobs.Jobs;
import net.momirealms.customcrops.api.integration.LevelInterface;
import org.bukkit.entity.Player;
public class EcoJobsImpl implements LevelInterface {
@Override
public void addXp(Player player, String target, double amount) {
for (Job job : EcoJobsAPI.getActiveJobs(player)) {
if (job.getId().equals(target)) {
EcoJobsAPI.giveJobExperience(player, job, amount);
}
}
}
@Override
public int getLevel(Player player, String target) {
Job job = Jobs.getByID(target);
if (job == null) return 0;
return EcoJobsAPI.getJobLevel(player, job);
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.level;
import com.willfp.ecoskills.api.EcoSkillsAPI;
import com.willfp.ecoskills.skills.Skills;
import net.momirealms.customcrops.api.integration.LevelInterface;
import org.bukkit.entity.Player;
import java.util.Objects;
public class EcoSkillsImpl implements LevelInterface {
@Override
public void addXp(Player player, String target, double amount) {
EcoSkillsAPI.gainSkillXP(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target)), amount);
}
@Override
public int getLevel(Player player, String target) {
return EcoSkillsAPI.getSkillLevel(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target)));
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.level;
import com.gamingmesh.jobs.Jobs;
import com.gamingmesh.jobs.container.Job;
import com.gamingmesh.jobs.container.JobProgression;
import com.gamingmesh.jobs.container.JobsPlayer;
import net.momirealms.customcrops.api.integration.LevelInterface;
import org.bukkit.entity.Player;
import java.util.List;
public class JobsRebornImpl implements LevelInterface {
@Override
public void addXp(Player player, String target, double amount) {
JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player);
if (jobsPlayer != null) {
List<JobProgression> jobs = jobsPlayer.getJobProgression();
Job job = Jobs.getJob(target);
for (JobProgression progression : jobs)
if (progression.getJob().equals(job))
progression.addExperience(amount);
}
}
@Override
public int getLevel(Player player, String target) {
JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player);
if (jobsPlayer != null) {
List<JobProgression> jobs = jobsPlayer.getJobProgression();
Job job = Jobs.getJob(target);
for (JobProgression progression : jobs)
if (progression.getJob().equals(job))
return progression.getLevel();
}
return 0;
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.level;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.EXPSource;
import net.momirealms.customcrops.api.integration.LevelInterface;
import org.bukkit.entity.Player;
public class MMOCoreImpl implements LevelInterface {
@Override
public void addXp(Player player, String target, double amount) {
MMOCore.plugin.professionManager.get(target).giveExperience(PlayerData.get(player), amount, null ,EXPSource.OTHER);
}
@Override
public int getLevel(Player player, String target) {
return PlayerData.get(player).getCollectionSkills().getLevel(MMOCore.plugin.professionManager.get(target));
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.level;
import com.gmail.nossr50.api.ExperienceAPI;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import net.momirealms.customcrops.api.integration.LevelInterface;
import org.bukkit.entity.Player;
public class McMMOImpl implements LevelInterface {
@Override
public void addXp(Player player, String target, double amount) {
ExperienceAPI.addRawXP(player, target, (float) amount, "UNKNOWN");
}
@Override
public int getLevel(Player player, String target) {
return ExperienceAPI.getLevel(player, PrimarySkillType.valueOf(target));
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.papi;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.manager.MessageManager;
import net.momirealms.customcrops.api.util.LogUtils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CCPapi extends PlaceholderExpansion {
private final CustomCropsPlugin plugin;
public CCPapi(CustomCropsPlugin plugin) {
this.plugin = plugin;
}
public void load() {
super.register();
}
public void unload() {
super.unregister();
}
@Override
public @NotNull String getIdentifier() {
return "customcrops";
}
@Override
public @NotNull String getAuthor() {
return "XiaoMoMi";
}
@Override
public @NotNull String getVersion() {
return "3.4";
}
@Override
public boolean persist() {
return true;
}
@Override
public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) {
String[] split = params.split("_", 2);
Player player = offlinePlayer.getPlayer();
if (player == null)
return null;
switch (split[0]) {
case "season" -> {
if (split.length == 1) {
return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(player.getWorld()));
} else {
try {
return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(Bukkit.getWorld(split[1])));
} catch (NullPointerException e) {
LogUtils.severe("World " + split[1] + " does not exist");
e.printStackTrace();
}
}
}
case "date" -> {
if (split.length == 1) {
return String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(player.getWorld()));
} else {
try {
return String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(Bukkit.getWorld(split[1])));
} catch (NullPointerException e) {
LogUtils.severe("World " + split[1] + " does not exist");
e.printStackTrace();
}
}
}
}
return null;
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.papi;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
public class ParseUtils {
public static String setPlaceholders(Player player, String text) {
return PlaceholderAPI.setPlaceholders(player, text);
}
public static String setPlaceholders(OfflinePlayer player, String text) {
return PlaceholderAPI.setPlaceholders(player, text);
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.quest;
import io.github.battlepass.BattlePlugin;
import io.github.battlepass.api.events.server.PluginReloadEvent;
import net.advancedplugins.bp.impl.actions.ActionRegistry;
import net.advancedplugins.bp.impl.actions.external.executor.ActionQuestExecutor;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.event.CropBreakEvent;
import net.momirealms.customcrops.api.event.CropPlantEvent;
import net.momirealms.customcrops.api.mechanic.item.Crop;
import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
public class BattlePassHook implements Listener {
public BattlePassHook() {
Bukkit.getPluginManager().registerEvents(this, CustomCropsPlugin.get());
}
public void register() {
ActionRegistry actionRegistry = BattlePlugin.getPlugin().getActionRegistry();
actionRegistry.hook("customcrops", BPHarvestCropsQuest::new);
}
@EventHandler(ignoreCancelled = true)
public void onBattlePassReload(PluginReloadEvent event){
register();
}
private static class BPHarvestCropsQuest extends ActionQuestExecutor {
public BPHarvestCropsQuest(JavaPlugin plugin) {
super(plugin, "customcrops");
}
@EventHandler (ignoreCancelled = true)
public void onBreakCrop(CropBreakEvent event) {
Player player = event.getPlayer();
if (player == null) return;
WorldCrop worldCrop = event.getWorldCrop();
if (worldCrop == null) return;
String id = worldCrop.getConfig().getStageItemByPoint(worldCrop.getPoint());
if (id.contains(":")) {
id = id.split(":")[1];
}
// Harvest crops
this.executionBuilder("harvest")
.player(player)
.root(id)
.progress(1)
.buildAndExecute();
}
@EventHandler (ignoreCancelled = true)
public void onPlantCrop(CropPlantEvent event){
Player player = event.getPlayer();
Crop crop = event.getCrop();
// Harvest crops
this.executionBuilder("plant")
.player(player)
.root(crop.getKey())
.progress(1)
.buildAndExecute();
}
}
}

View File

@@ -1,199 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.quest;
import net.momirealms.customcrops.api.event.CropBreakEvent;
import net.momirealms.customcrops.api.event.CropPlantEvent;
import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop;
import net.momirealms.customcrops.api.util.LogUtils;
import org.betonquest.betonquest.BetonQuest;
import org.betonquest.betonquest.Instruction;
import org.betonquest.betonquest.api.CountingObjective;
import org.betonquest.betonquest.api.config.quest.QuestPackage;
import org.betonquest.betonquest.api.profiles.OnlineProfile;
import org.betonquest.betonquest.api.profiles.Profile;
import org.betonquest.betonquest.exceptions.InstructionParseException;
import org.betonquest.betonquest.exceptions.QuestRuntimeException;
import org.betonquest.betonquest.instruction.variable.VariableNumber;
import org.betonquest.betonquest.instruction.variable.location.VariableLocation;
import org.betonquest.betonquest.utils.PlayerConverter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import java.util.Collections;
import java.util.HashSet;
@SuppressWarnings("DuplicatedCode")
public class BetonQuestHook {
public static void register() {
BetonQuest.getInstance().registerObjectives("customcrops_harvest", HarvestObjective.class);
BetonQuest.getInstance().registerObjectives("customcrops_plant", PlantObjective.class);
}
public static class HarvestObjective extends CountingObjective implements Listener {
private final VariableLocation playerLocation;
private final VariableNumber rangeVar;
private final HashSet<String> crop_ids;
public HarvestObjective(Instruction instruction) throws InstructionParseException {
super(instruction, "crop_to_harvest");
crop_ids = new HashSet<>();
Collections.addAll(crop_ids, instruction.getArray());
targetAmount = instruction.getVarNum(VariableNumber.NOT_LESS_THAN_ONE_CHECKER);
final QuestPackage pack = instruction.getPackage();
final String loc = instruction.getOptional("playerLocation");
final String range = instruction.getOptional("range");
if (loc != null && range != null) {
playerLocation = new VariableLocation(BetonQuest.getInstance().getVariableProcessor(), pack, loc);
rangeVar = new VariableNumber(BetonQuest.getInstance().getVariableProcessor(), pack, range);
} else {
playerLocation = null;
rangeVar = null;
}
}
@EventHandler (ignoreCancelled = true)
public void onBreakCrop(CropBreakEvent event) {
if (event.getPlayer() == null) {
return;
}
WorldCrop crop = event.getWorldCrop();
if (crop == null) return;
OnlineProfile onlineProfile = PlayerConverter.getID(event.getPlayer());
if (!containsPlayer(onlineProfile)) {
return;
}
if (isInvalidLocation(event, onlineProfile)) {
return;
}
if (this.crop_ids.contains(crop.getConfig().getStageItemByPoint(crop.getPoint())) && this.checkConditions(onlineProfile)) {
getCountingData(onlineProfile).progress(1);
completeIfDoneOrNotify(onlineProfile);
}
}
private boolean isInvalidLocation(CropBreakEvent event, final Profile profile) {
if (playerLocation == null || rangeVar == null) {
return false;
}
final Location targetLocation;
try {
targetLocation = playerLocation.getValue(profile);
} catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) {
LogUtils.warn(e.getMessage());
return true;
}
int range;
try {
range = rangeVar.getValue(profile).intValue();
} catch (QuestRuntimeException e) {
throw new RuntimeException(e);
}
final Location playerLoc = event.getPlayer().getLocation();
return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range;
}
@Override
public void start() {
Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance());
}
@Override
public void stop() {
HandlerList.unregisterAll(this);
}
}
public static class PlantObjective extends CountingObjective implements Listener {
private final VariableLocation playerLocation;
private final VariableNumber rangeVar;
private final HashSet<String> crops;
public PlantObjective(Instruction instruction) throws InstructionParseException {
super(instruction, "crop_to_plant");
crops = new HashSet<>();
Collections.addAll(crops, instruction.getArray());
targetAmount = instruction.getVarNum(VariableNumber.NOT_LESS_THAN_ONE_CHECKER);
final QuestPackage pack = instruction.getPackage();
final String loc = instruction.getOptional("playerLocation");
final String range = instruction.getOptional("range");
if (loc != null && range != null) {
playerLocation = new VariableLocation(BetonQuest.getInstance().getVariableProcessor(), pack, loc);
rangeVar = new VariableNumber(BetonQuest.getInstance().getVariableProcessor(), pack, range);
} else {
playerLocation = null;
rangeVar = null;
}
}
@EventHandler (ignoreCancelled = true)
public void onPlantCrop(CropPlantEvent event) {
OnlineProfile onlineProfile = PlayerConverter.getID(event.getPlayer());
if (!containsPlayer(onlineProfile)) {
return;
}
if (isInvalidLocation(event, onlineProfile)) {
return;
}
if (this.crops.contains(event.getCrop().getKey()) && this.checkConditions(onlineProfile)) {
getCountingData(onlineProfile).progress(1);
completeIfDoneOrNotify(onlineProfile);
}
}
private boolean isInvalidLocation(CropPlantEvent event, final Profile profile) {
if (playerLocation == null || rangeVar == null) {
return false;
}
final Location targetLocation;
try {
targetLocation = playerLocation.getValue(profile);
} catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) {
LogUtils.warn(e.getMessage());
return true;
}
int range;
try {
range = rangeVar.getValue(profile).intValue();
} catch (QuestRuntimeException e) {
throw new RuntimeException(e);
}
final Location playerLoc = event.getPlayer().getLocation();
return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range;
}
@Override
public void start() {
Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance());
}
@Override
public void stop() {
HandlerList.unregisterAll(this);
}
}
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.quest;
import com.electro2560.dev.cluescrolls.api.*;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.event.CropBreakEvent;
import net.momirealms.customcrops.api.event.CropPlantEvent;
import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class ClueScrollsHook implements Listener {
private final CustomClue harvestClue;
private final CustomClue plantClue;
public ClueScrollsHook() {
harvestClue = ClueScrollsAPI.getInstance().registerCustomClue(CustomCropsPlugin.getInstance(), "harvest", new ClueConfigData("id", DataType.STRING));
plantClue = ClueScrollsAPI.getInstance().registerCustomClue(CustomCropsPlugin.getInstance(), "plant", new ClueConfigData("id", DataType.STRING));
}
public void register() {
Bukkit.getPluginManager().registerEvents(this, CustomCropsPlugin.get());
}
@EventHandler (ignoreCancelled = true)
public void onBreakCrop(CropBreakEvent event) {
final Player player = event.getPlayer();
if (player == null) return;
WorldCrop crop = event.getWorldCrop();
if (crop == null) return;
harvestClue.handle(
player,
1,
new ClueDataPair("id", crop.getConfig().getStageItemByPoint(crop.getPoint()))
);
}
@EventHandler (ignoreCancelled = true)
public void onPlantCrop(CropPlantEvent event) {
plantClue.handle(
event.getPlayer(),
1,
new ClueDataPair("id", event.getCrop().getKey())
);
}
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.season;
import net.advancedplugins.seasons.Core;
import net.momirealms.customcrops.api.integration.SeasonInterface;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
public class AdvancedSeasonsImpl implements SeasonInterface {
@Override
public Season getSeason(@NotNull World world) {
net.advancedplugins.seasons.enums.Season season = Core.getSeasonHandler().getSeason(world);
if (season == null) {
return null;
}
return switch (season.getType()) {
case SPRING -> Season.SPRING;
case WINTER -> Season.WINTER;
case SUMMER -> Season.SUMMER;
case FALL -> Season.AUTUMN;
};
}
@Override
public int getDate(World world) {
return 0;
}
@Override
public void setSeason(World world, Season season) {
String seasonName = switch (season) {
case AUTUMN -> "FALL";
case WINTER -> "WINTER";
case SUMMER -> "SUMMER";
case SPRING -> "SPRING";
};
Core.getSeasonHandler().setSeason(net.advancedplugins.seasons.enums.Season.valueOf(seasonName), world.getName());
}
@Override
public void setDate(World world, int date) {
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.season;
import net.momirealms.customcrops.api.integration.SeasonInterface;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.manager.WorldManager;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import net.momirealms.customcrops.api.mechanic.world.level.WorldInfoData;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;
public class InBuiltSeason implements SeasonInterface {
private final WorldManager worldManager;
public InBuiltSeason(WorldManager worldManager) {
this.worldManager = worldManager;
}
@Override
public @Nullable Season getSeason(World world) {
return worldManager
.getCustomCropsWorld(world)
.map(CustomCropsWorld::getSeason)
.orElse(null);
}
@Override
public int getDate(World world) {
if (ConfigManager.syncSeasons())
world = ConfigManager.referenceWorld();
if (world == null)
return 0;
return worldManager
.getCustomCropsWorld(world)
.map(CustomCropsWorld::getDate)
.orElse(0);
}
@Override
public void setSeason(World world, Season season) {
worldManager.getCustomCropsWorld(world)
.ifPresent(customWorld -> {
WorldInfoData infoData = customWorld.getInfoData();
infoData.setSeason(season);
});
}
@Override
public void setDate(World world, int date) {
worldManager.getCustomCropsWorld(world)
.ifPresent(customWorld -> {
WorldInfoData infoData = customWorld.getInfoData();
infoData.setDate(date);
});
}
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright (C) <2022> <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.compatibility.season;
import me.casperge.realisticseasons.api.SeasonsAPI;
import me.casperge.realisticseasons.calendar.Date;
import net.momirealms.customcrops.api.integration.SeasonInterface;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;
public class RealisticSeasonsImpl implements SeasonInterface {
private final SeasonsAPI api;
public RealisticSeasonsImpl() {
this.api = SeasonsAPI.getInstance();
}
@Override
public @Nullable Season getSeason(World world) {
return switch (api.getSeason(world)) {
case WINTER -> Season.WINTER;
case SPRING -> Season.SPRING;
case SUMMER -> Season.SUMMER;
case FALL -> Season.AUTUMN;
case DISABLED, RESTORE -> null;
};
}
@Override
public int getDate(World world) {
return api.getDate(world).getDay();
}
@Override
public void setSeason(World world, Season season) {
me.casperge.realisticseasons.season.Season rsSeason = switch (season) {
case AUTUMN -> me.casperge.realisticseasons.season.Season.FALL;
case SUMMER -> me.casperge.realisticseasons.season.Season.SUMMER;
case WINTER -> me.casperge.realisticseasons.season.Season.WINTER;
case SPRING -> me.casperge.realisticseasons.season.Season.SPRING;
};
api.setSeason(world, rsSeason);
}
@Override
public void setDate(World world, int date) {
Date rsDate = api.getDate(world);
api.setDate(world, new Date(date, rsDate.getMonth(), rsDate.getYear()));
}
}

View File

@@ -1,41 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.classpath;
import java.nio.file.Path;
/**
* Interface which allows access to add URLs to the plugin classpath at runtime.
*/
public interface ClassPathAppender extends AutoCloseable {
void addJarToClasspath(Path file);
@Override
default void close() {
}
}

View File

@@ -1,62 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.classpath;
import net.momirealms.customcrops.libraries.loader.JarInJarClassLoader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
public class JarInJarClassPathAppender implements ClassPathAppender {
private final JarInJarClassLoader classLoader;
public JarInJarClassPathAppender(ClassLoader classLoader) {
if (!(classLoader instanceof JarInJarClassLoader)) {
throw new IllegalArgumentException("Loader is not a JarInJarClassLoader: " + classLoader.getClass().getName());
}
this.classLoader = (JarInJarClassLoader) classLoader;
}
@Override
public void addJarToClasspath(Path file) {
try {
this.classLoader.addJarToClasspath(file.toUri().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() {
this.classLoader.deleteJarResource();
try {
this.classLoader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -1,190 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.classpath;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
/**
* Provides access to {@link URLClassLoader}#addURL.
*/
public abstract class URLClassLoaderAccess {
/**
* Creates a {@link URLClassLoaderAccess} for the given class loader.
*
* @param classLoader the class loader
* @return the access object
*/
public static URLClassLoaderAccess create(URLClassLoader classLoader) {
if (Reflection.isSupported()) {
return new Reflection(classLoader);
} else if (Unsafe.isSupported()) {
return new Unsafe(classLoader);
} else {
return Noop.INSTANCE;
}
}
private final URLClassLoader classLoader;
protected URLClassLoaderAccess(URLClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Adds the given URL to the class loader.
*
* @param url the URL to add
*/
public abstract void addURL(@NonNull URL url);
private static void throwError(Throwable cause) throws UnsupportedOperationException {
throw new UnsupportedOperationException("CustomCrops is unable to inject into the plugin URLClassLoader.\n" +
"You may be able to fix this problem by adding the following command-line argument " +
"directly after the 'java' command in your start script: \n'--add-opens java.base/java.lang=ALL-UNNAMED'", cause);
}
/**
* Accesses using reflection, not supported on Java 9+.
*/
private static class Reflection extends URLClassLoaderAccess {
private static final Method ADD_URL_METHOD;
static {
Method addUrlMethod;
try {
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
} catch (Exception e) {
addUrlMethod = null;
}
ADD_URL_METHOD = addUrlMethod;
}
private static boolean isSupported() {
return ADD_URL_METHOD != null;
}
Reflection(URLClassLoader classLoader) {
super(classLoader);
}
@Override
public void addURL(@NonNull URL url) {
try {
ADD_URL_METHOD.invoke(super.classLoader, url);
} catch (ReflectiveOperationException e) {
URLClassLoaderAccess.throwError(e);
}
}
}
/**
* Accesses using sun.misc.Unsafe, supported on Java 9+.
*
* @author Vaishnav Anil (https://github.com/slimjar/slimjar)
*/
private static class Unsafe extends URLClassLoaderAccess {
private static final sun.misc.Unsafe UNSAFE;
static {
sun.misc.Unsafe unsafe;
try {
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
} catch (Throwable t) {
unsafe = null;
}
UNSAFE = unsafe;
}
private static boolean isSupported() {
return UNSAFE != null;
}
private final Collection<URL> unopenedURLs;
private final Collection<URL> pathURLs;
@SuppressWarnings("unchecked")
Unsafe(URLClassLoader classLoader) {
super(classLoader);
Collection<URL> unopenedURLs;
Collection<URL> pathURLs;
try {
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
} catch (Throwable e) {
unopenedURLs = null;
pathURLs = null;
}
this.unopenedURLs = unopenedURLs;
this.pathURLs = pathURLs;
}
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
Field field = clazz.getDeclaredField(name);
long offset = UNSAFE.objectFieldOffset(field);
return UNSAFE.getObject(object, offset);
}
@Override
public void addURL(@NonNull URL url) {
if (this.unopenedURLs == null || this.pathURLs == null) {
URLClassLoaderAccess.throwError(new NullPointerException("unopenedURLs or pathURLs"));
}
synchronized (this.unopenedURLs) {
this.unopenedURLs.add(url);
this.pathURLs.add(url);
}
}
}
private static class Noop extends URLClassLoaderAccess {
private static final Noop INSTANCE = new Noop();
private Noop() {
super(null);
}
@Override
public void addURL(@NonNull URL url) {
URLClassLoaderAccess.throwError(null);
}
}
}

View File

@@ -1,211 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies;
import com.google.common.collect.ImmutableList;
import net.momirealms.customcrops.libraries.dependencies.relocation.Relocation;
import org.jetbrains.annotations.Nullable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Locale;
/**
* The dependencies used by CustomCrops.
*/
public enum Dependency {
ASM(
"org.ow2.asm",
"asm",
"9.7",
null,
"asm"
),
ASM_COMMONS(
"org.ow2.asm",
"asm-commons",
"9.7",
null,
"asm-commons"
),
JAR_RELOCATOR(
"me.lucko",
"jar-relocator",
"1.7",
null,
"jar-relocator"
),
COMMAND_API(
"dev{}jorel",
"commandapi-bukkit-shade",
"9.5.3",
null,
"commandapi-bukkit",
Relocation.of("commandapi", "dev{}jorel{}commandapi")
),
COMMAND_API_MOJMAP(
"dev{}jorel",
"commandapi-bukkit-shade-mojang-mapped",
"9.5.3",
null,
"commandapi-bukkit-mojang-mapped",
Relocation.of("commandapi", "dev{}jorel{}commandapi")
),
BOOSTED_YAML(
"dev{}dejvokep",
"boosted-yaml",
"1.3.6",
null,
"boosted-yaml",
Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml")
),
H2_DRIVER(
"com.h2database",
"h2",
"2.2.224",
null,
"h2database"
),
SQLITE_DRIVER(
"org.xerial",
"sqlite-jdbc",
"3.45.1.0",
null,
"sqlite-jdbc"
),
SLF4J_SIMPLE(
"org.slf4j",
"slf4j-simple",
"2.0.12",
null,
"slf4j-simple"
),
SLF4J_API(
"org.slf4j",
"slf4j-api",
"2.0.12",
null,
"slf4j-api"
),
BSTATS_BASE(
"org{}bstats",
"bstats-base",
"3.0.2",
null,
"bstats-base",
Relocation.of("bstats", "org{}bstats")
),
BSTATS_BUKKIT(
"org{}bstats",
"bstats-bukkit",
"3.0.2",
null,
"bstats-bukkit",
Relocation.of("bstats", "org{}bstats")
),
GSON(
"com.google.code.gson",
"gson",
"2.10.1",
null,
"gson"
),
EXP4J(
"net{}objecthunter",
"exp4j",
"0.4.8",
null,
"exp4j",
Relocation.of("exp4j", "net{}objecthunter{}exp4j")
);
private final String mavenRepoPath;
private final String version;
private final List<Relocation> relocations;
private final String repo;
private final String artifact;
private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar";
Dependency(String groupId, String artifactId, String version, String repo, String artifact) {
this(groupId, artifactId, version, repo, artifact, new Relocation[0]);
}
Dependency(String groupId, String artifactId, String version, String repo, String artifact, Relocation... relocations) {
this.mavenRepoPath = String.format(MAVEN_FORMAT,
rewriteEscaping(groupId).replace(".", "/"),
rewriteEscaping(artifactId),
version,
rewriteEscaping(artifactId),
version
);
this.version = version;
this.relocations = ImmutableList.copyOf(relocations);
this.repo = repo;
this.artifact = artifact;
}
private static String rewriteEscaping(String s) {
return s.replace("{}", ".");
}
public String getFileName(String classifier) {
String name = artifact.toLowerCase(Locale.ROOT).replace('_', '-');
String extra = classifier == null || classifier.isEmpty()
? ""
: "-" + classifier;
return name + "-" + this.version + extra + ".jar";
}
String getMavenRepoPath() {
return this.mavenRepoPath;
}
public List<Relocation> getRelocations() {
return this.relocations;
}
/**
* Creates a {@link MessageDigest} suitable for computing the checksums
* of dependencies.
*
* @return the digest
*/
public static MessageDigest createDigest() {
try {
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
@Nullable
public String getRepo() {
return repo;
}
}

View File

@@ -1,48 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies;
/**
* Exception thrown if a dependency cannot be downloaded.
*/
public class DependencyDownloadException extends Exception {
public DependencyDownloadException() {
}
public DependencyDownloadException(String message) {
super(message);
}
public DependencyDownloadException(String message, Throwable cause) {
super(message, cause);
}
public DependencyDownloadException(Throwable cause) {
super(cause);
}
}

View File

@@ -1,53 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies;
import java.util.Collection;
import java.util.Set;
/**
* Loads and manages runtime dependencies for the plugin.
*/
public interface DependencyManager extends AutoCloseable {
/**
* Loads dependencies.
*
* @param dependencies the dependencies to load
*/
void loadDependencies(Collection<Dependency> dependencies);
/**
* Obtains an isolated classloader containing the given dependencies.
*
* @param dependencies the dependencies
* @return the classloader
*/
ClassLoader obtainClassLoaderWith(Set<Dependency> dependencies);
@Override
void close();
}

View File

@@ -1,229 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies;
import com.google.common.collect.ImmutableSet;
import net.momirealms.customcrops.CustomCropsPluginImpl;
import net.momirealms.customcrops.api.util.LogUtils;
import net.momirealms.customcrops.libraries.classpath.ClassPathAppender;
import net.momirealms.customcrops.libraries.dependencies.classloader.IsolatedClassLoader;
import net.momirealms.customcrops.libraries.dependencies.relocation.Relocation;
import net.momirealms.customcrops.libraries.dependencies.relocation.RelocationHandler;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CountDownLatch;
/**
* Loads and manages runtime dependencies for the plugin.
*/
public class DependencyManagerImpl implements DependencyManager {
/** A registry containing plugin specific behaviour for dependencies. */
private final DependencyRegistry registry;
/** The path where library jars are cached. */
private final Path cacheDirectory;
/** The classpath appender to preload dependencies into */
private final ClassPathAppender classPathAppender;
/** A map of dependencies which have already been loaded. */
private final EnumMap<Dependency, Path> loaded = new EnumMap<>(Dependency.class);
/** A map of isolated classloaders which have been created. */
private final Map<ImmutableSet<Dependency>, IsolatedClassLoader> loaders = new HashMap<>();
/** Cached relocation handler instance. */
private @MonotonicNonNull RelocationHandler relocationHandler = null;
public DependencyManagerImpl(CustomCropsPluginImpl plugin, ClassPathAppender classPathAppender) {
this.registry = new DependencyRegistry();
this.cacheDirectory = setupCacheDirectory(plugin);
this.classPathAppender = classPathAppender;
this.relocationHandler = new RelocationHandler(this);
}
@Override
public ClassLoader obtainClassLoaderWith(Set<Dependency> dependencies) {
ImmutableSet<Dependency> set = ImmutableSet.copyOf(dependencies);
for (Dependency dependency : dependencies) {
if (!this.loaded.containsKey(dependency)) {
throw new IllegalStateException("Dependency " + dependency + " is not loaded.");
}
}
synchronized (this.loaders) {
IsolatedClassLoader classLoader = this.loaders.get(set);
if (classLoader != null) {
return classLoader;
}
URL[] urls = set.stream()
.map(this.loaded::get)
.map(file -> {
try {
return file.toUri().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
})
.toArray(URL[]::new);
classLoader = new IsolatedClassLoader(urls);
this.loaders.put(set, classLoader);
return classLoader;
}
}
@Override
public void loadDependencies(Collection<Dependency> dependencies) {
CountDownLatch latch = new CountDownLatch(dependencies.size());
for (Dependency dependency : dependencies) {
if (this.loaded.containsKey(dependency)) {
latch.countDown();
continue;
}
try {
loadDependency(dependency);
} catch (Throwable e) {
new RuntimeException("Unable to load dependency " + dependency.name(), e).printStackTrace();
} finally {
latch.countDown();
}
}
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void loadDependency(Dependency dependency) throws Exception {
if (this.loaded.containsKey(dependency)) {
return;
}
Path file = remapDependency(dependency, downloadDependency(dependency));
this.loaded.put(dependency, file);
if (this.classPathAppender != null && this.registry.shouldAutoLoad(dependency)) {
this.classPathAppender.addJarToClasspath(file);
}
}
private Path downloadDependency(Dependency dependency) throws DependencyDownloadException {
Path file = this.cacheDirectory.resolve(dependency.getFileName(null));
// if the file already exists, don't attempt to re-download it.
if (Files.exists(file)) {
return file;
}
DependencyDownloadException lastError = null;
String fileName = dependency.getFileName(null);
String forceRepo = dependency.getRepo();
if (forceRepo == null) {
// attempt to download the dependency from each repo in order.
for (DependencyRepository repo : DependencyRepository.values()) {
try {
LogUtils.info("Downloading dependency(" + fileName + ") from " + repo.getUrl() + dependency.getMavenRepoPath());
repo.download(dependency, file);
LogUtils.info("Successfully downloaded " + fileName);
return file;
} catch (DependencyDownloadException e) {
lastError = e;
}
}
} else {
DependencyRepository repository = DependencyRepository.getByID(forceRepo);
if (repository != null) {
try {
LogUtils.info("Downloading dependency(" + fileName + ") from " + repository.getUrl() + dependency.getMavenRepoPath());
repository.download(dependency, file);
LogUtils.info("Successfully downloaded " + fileName);
return file;
} catch (DependencyDownloadException e) {
lastError = e;
}
}
}
throw Objects.requireNonNull(lastError);
}
private Path remapDependency(Dependency dependency, Path normalFile) throws Exception {
List<Relocation> rules = new ArrayList<>(dependency.getRelocations());
if (rules.isEmpty()) {
return normalFile;
}
Path remappedFile = this.cacheDirectory.resolve(dependency.getFileName(DependencyRegistry.isGsonRelocated() ? "remapped-legacy" : "remapped"));
// if the remapped source exists already, just use that.
if (Files.exists(remappedFile)) {
return remappedFile;
}
LogUtils.info("Remapping " + dependency.getFileName(null));
relocationHandler.remap(normalFile, remappedFile, rules);
LogUtils.info("Successfully remapped " + dependency.getFileName(null));
return remappedFile;
}
private static Path setupCacheDirectory(CustomCropsPluginImpl plugin) {
File folder = new File(plugin.getDataFolder(), "libs");
folder.mkdirs();
return folder.toPath();
}
@Override
public void close() {
IOException firstEx = null;
for (IsolatedClassLoader loader : this.loaders.values()) {
try {
loader.close();
} catch (IOException ex) {
if (firstEx == null) {
firstEx = ex;
} else {
firstEx.addSuppressed(ex);
}
}
}
if (firstEx != null) {
firstEx.printStackTrace();
}
}
}

View File

@@ -1,62 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies;
import com.google.gson.JsonElement;
/**
* Applies CustomCrops specific behaviour for {@link Dependency}s.
*/
public class DependencyRegistry {
public boolean shouldAutoLoad(Dependency dependency) {
return switch (dependency) {
// all used within 'isolated' classloaders, and are therefore not
// relocated.
case ASM, ASM_COMMONS, JAR_RELOCATOR, H2_DRIVER, SQLITE_DRIVER -> false;
default -> true;
};
}
@SuppressWarnings("ConstantConditions")
public static boolean isGsonRelocated() {
return JsonElement.class.getName().startsWith("net.momirealms");
}
private static boolean classExists(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
private static boolean slf4jPresent() {
return classExists("org.slf4j.Logger") && classExists("org.slf4j.LoggerFactory");
}
}

View File

@@ -1,150 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies;
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Represents a repository which contains {@link Dependency}s.
*/
public enum DependencyRepository {
/**
* Maven Central
*/
MAVEN_CENTRAL("maven", "https://repo1.maven.org/maven2/") {
@Override
protected URLConnection openConnection(Dependency dependency) throws IOException {
URLConnection connection = super.openConnection(dependency);
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
return connection;
}
},
/**
* Maven Central Mirror
*/
MAVEN_CENTRAL_MIRROR("aliyun", "https://maven.aliyun.com/repository/public/"),
/**
* Code MC
*/
CODE_MC("codemc", "https://repo.codemc.io/repository/maven-public/"),
/**
* Jitpack
*/
JITPACK("jitpack", "https://jitpack.io/");
private final String url;
private final String id;
DependencyRepository(String id, String url) {
this.url = url;
this.id = id;
}
public String getUrl() {
return url;
}
public static DependencyRepository getByID(String id) {
for (DependencyRepository repository : values()) {
if (id.equals(repository.id)) {
return repository;
}
}
return null;
}
/**
* Opens a connection to the given {@code dependency}.
*
* @param dependency the dependency to download
* @return the connection
* @throws IOException if unable to open a connection
*/
protected URLConnection openConnection(Dependency dependency) throws IOException {
URL dependencyUrl = new URL(this.url + dependency.getMavenRepoPath());
return dependencyUrl.openConnection();
}
/**
* Downloads the raw bytes of the {@code dependency}.
*
* @param dependency the dependency to download
* @return the downloaded bytes
* @throws DependencyDownloadException if unable to download
*/
public byte[] downloadRaw(Dependency dependency) throws DependencyDownloadException {
try {
URLConnection connection = openConnection(dependency);
try (InputStream in = connection.getInputStream()) {
byte[] bytes = ByteStreams.toByteArray(in);
if (bytes.length == 0) {
throw new DependencyDownloadException("Empty stream");
}
return bytes;
}
} catch (Exception e) {
throw new DependencyDownloadException(e);
}
}
/**
* @param dependency the dependency to download
* @return the downloaded bytes
* @throws DependencyDownloadException if unable to download
*/
public byte[] download(Dependency dependency) throws DependencyDownloadException {
return downloadRaw(dependency);
}
/**
* Downloads the the {@code dependency} to the {@code file}, ensuring the
* downloaded bytes match the checksum.
*
* @param dependency the dependency to download
* @param file the file to write to
* @throws DependencyDownloadException if unable to download
*/
public void download(Dependency dependency, Path file) throws DependencyDownloadException {
try {
Files.write(file, download(dependency));
} catch (IOException e) {
throw new DependencyDownloadException(e);
}
}
public String getId() {
return id;
}
}

View File

@@ -1,54 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies.classloader;
import java.net.URL;
import java.net.URLClassLoader;
/**
* A classloader "isolated" from the rest of the Minecraft server.
*
* <p>Used to load specific CustomCrops dependencies without causing conflicts
* with other plugins, or libraries provided by the server implementation.</p>
*/
public class IsolatedClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
public IsolatedClassLoader(URL[] urls) {
/*
* ClassLoader#getSystemClassLoader returns the AppClassLoader
*
* Calling #getParent on this returns the ExtClassLoader (Java 8) or
* the PlatformClassLoader (Java 9). Since we want this classloader to
* be isolated from the Minecraft server (the app), we set the parent
* to be the platform class loader.
*/
super(urls, ClassLoader.getSystemClassLoader().getParent());
}
}

View File

@@ -1,66 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies.relocation;
import java.util.Objects;
public final class Relocation {
private static final String RELOCATION_PREFIX = "net.momirealms.customcrops.libraries.";
public static Relocation of(String id, String pattern) {
return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id);
}
private final String pattern;
private final String relocatedPattern;
private Relocation(String pattern, String relocatedPattern) {
this.pattern = pattern;
this.relocatedPattern = relocatedPattern;
}
public String getPattern() {
return this.pattern;
}
public String getRelocatedPattern() {
return this.relocatedPattern;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Relocation that = (Relocation) o;
return Objects.equals(this.pattern, that.pattern) &&
Objects.equals(this.relocatedPattern, that.relocatedPattern);
}
@Override
public int hashCode() {
return Objects.hash(this.pattern, this.relocatedPattern);
}
}

View File

@@ -1,91 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies.relocation;
import net.momirealms.customcrops.libraries.dependencies.Dependency;
import net.momirealms.customcrops.libraries.dependencies.DependencyManager;
import net.momirealms.customcrops.libraries.dependencies.classloader.IsolatedClassLoader;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.*;
/**
* Handles class runtime relocation of packages in downloaded dependencies
*/
public class RelocationHandler {
public static final Set<Dependency> DEPENDENCIES = EnumSet.of(Dependency.ASM, Dependency.ASM_COMMONS, Dependency.JAR_RELOCATOR);
private static final String JAR_RELOCATOR_CLASS = "me.lucko.jarrelocator.JarRelocator";
private static final String JAR_RELOCATOR_RUN_METHOD = "run";
private final Constructor<?> jarRelocatorConstructor;
private final Method jarRelocatorRunMethod;
public RelocationHandler(DependencyManager dependencyManager) {
ClassLoader classLoader = null;
try {
// download the required dependencies for remapping
dependencyManager.loadDependencies(DEPENDENCIES);
// get a classloader containing the required dependencies as sources
classLoader = dependencyManager.obtainClassLoaderWith(DEPENDENCIES);
// load the relocator class
Class<?> jarRelocatorClass = classLoader.loadClass(JAR_RELOCATOR_CLASS);
// prepare the the reflected constructor & method instances
this.jarRelocatorConstructor = jarRelocatorClass.getDeclaredConstructor(File.class, File.class, Map.class);
this.jarRelocatorConstructor.setAccessible(true);
this.jarRelocatorRunMethod = jarRelocatorClass.getDeclaredMethod(JAR_RELOCATOR_RUN_METHOD);
this.jarRelocatorRunMethod.setAccessible(true);
} catch (Exception e) {
try {
if (classLoader instanceof IsolatedClassLoader) {
((IsolatedClassLoader) classLoader).close();
}
} catch (IOException ex) {
e.addSuppressed(ex);
}
throw new RuntimeException(e);
}
}
public void remap(Path input, Path output, List<Relocation> relocations) throws Exception {
Map<String, String> mappings = new HashMap<>();
for (Relocation relocation : relocations) {
mappings.put(relocation.getPattern(), relocation.getRelocatedPattern());
}
// create and invoke a new relocator
Object relocator = this.jarRelocatorConstructor.newInstance(input.toFile(), output.toFile(), mappings);
this.jarRelocatorRunMethod.invoke(relocator);
}
}

View File

@@ -1,37 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.dependencies.relocation;
public final class RelocationHelper {
// screw maven shade
public static final String OKIO_STRING = String.valueOf(new char[]{'o', 'k', 'i', 'o'});
public static final String OKHTTP3_STRING = String.valueOf(new char[]{'o', 'k', 'h', 't', 't', 'p', '3'});
private RelocationHelper() {
}
}

View File

@@ -1,155 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.loader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
/**
* Classloader that can load a jar from within another jar file.
*
* <p>The "loader" jar contains the loading code & public API classes,
* and is class-loaded by the platform.</p>
*
* <p>The inner "plugin" jar contains the plugin itself, and is class-loaded
* by the loading code & this classloader.</p>
*/
public class JarInJarClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
/**
* Creates a new jar-in-jar class loader.
*
* @param loaderClassLoader the loader plugin's classloader (setup and created by the platform)
* @param jarResourcePath the path to the jar-in-jar resource within the loader jar
* @throws LoadingException if something unexpectedly bad happens
*/
public JarInJarClassLoader(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException {
super(new URL[]{extractJar(loaderClassLoader, jarResourcePath)}, loaderClassLoader);
}
public void addJarToClasspath(URL url) {
addURL(url);
}
public void deleteJarResource() {
URL[] urls = getURLs();
if (urls.length == 0) {
return;
}
try {
Path path = Paths.get(urls[0].toURI());
Files.deleteIfExists(path);
} catch (Exception e) {
// ignore
}
}
/**
* Creates a new plugin instance.
*
* @param bootstrapClass the name of the bootstrap plugin class
* @param loaderPluginType the type of the loader plugin, the only parameter of the bootstrap
* plugin constructor
* @param loaderPlugin the loader plugin instance
* @param <T> the type of the loader plugin
* @return the instantiated bootstrap plugin
*/
public <T> LoaderBootstrap instantiatePlugin(String bootstrapClass, Class<T> loaderPluginType, T loaderPlugin) throws LoadingException {
Class<? extends LoaderBootstrap> plugin;
try {
plugin = loadClass(bootstrapClass).asSubclass(LoaderBootstrap.class);
} catch (ReflectiveOperationException e) {
throw new LoadingException("Unable to load bootstrap class", e);
}
Constructor<? extends LoaderBootstrap> constructor;
try {
constructor = plugin.getConstructor(loaderPluginType);
} catch (ReflectiveOperationException e) {
throw new LoadingException("Unable to get bootstrap constructor", e);
}
try {
return constructor.newInstance(loaderPlugin);
} catch (ReflectiveOperationException e) {
throw new LoadingException("Unable to create bootstrap plugin instance", e);
}
}
/**
* Extracts the "jar-in-jar" from the loader plugin into a temporary file,
* then returns a URL that can be used by the {@link JarInJarClassLoader}.
*
* @param loaderClassLoader the classloader for the "host" loader plugin
* @param jarResourcePath the inner jar resource path
* @return a URL to the extracted file
*/
private static URL extractJar(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException {
// get the jar-in-jar resource
URL jarInJar = loaderClassLoader.getResource(jarResourcePath);
if (jarInJar == null) {
throw new LoadingException("Could not locate jar-in-jar");
}
// create a temporary file
// on posix systems by default this is only read/writable by the process owner
Path path;
try {
path = Files.createTempFile("customcrops-jarinjar", ".jar.tmp");
} catch (IOException e) {
throw new LoadingException("Unable to create a temporary file", e);
}
// mark that the file should be deleted on exit
path.toFile().deleteOnExit();
// copy the jar-in-jar to the temporary file path
try (InputStream in = jarInJar.openStream()) {
Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new LoadingException("Unable to copy jar-in-jar to temporary path", e);
}
try {
return path.toUri().toURL();
} catch (MalformedURLException e) {
throw new LoadingException("Unable to get URL from path", e);
}
}
}

View File

@@ -1,39 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.loader;
/**
* Minimal bootstrap plugin, called by the loader plugin.
*/
public interface LoaderBootstrap {
void onLoad();
default void onEnable() {}
default void onDisable() {}
}

View File

@@ -1,41 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.momirealms.customcrops.libraries.loader;
/**
* Runtime exception used if there is a problem during loading
*/
public class LoadingException extends RuntimeException {
public LoadingException(String message) {
super(message);
}
public LoadingException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,214 +0,0 @@
/*
* Copyright (C) <2022> <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.manager;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.title.Title;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.manager.AdventureManager;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.manager.MessageManager;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import java.time.Duration;
public class AdventureManagerImpl extends AdventureManager {
private final CustomCropsPlugin plugin;
private BukkitAudiences audiences;
public AdventureManagerImpl(CustomCropsPlugin plugin) {
this.plugin = plugin;
init();
}
@Override
public void init() {
this.audiences = BukkitAudiences.create(plugin);
}
@Override
public void disable() {
if (this.audiences != null)
this.audiences.close();
}
@Override
public void sendMessage(CommandSender sender, String text) {
if (text == null) return;
if (sender instanceof Player player) sendPlayerMessage(player, text);
else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(text);
}
@Override
public void sendMessageWithPrefix(CommandSender sender, String text) {
if (text == null) return;
if (sender instanceof Player player) sendPlayerMessage(player, MessageManager.prefix() + text);
else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(MessageManager.prefix() + text);
}
@Override
public void sendConsoleMessage(String text) {
if (text == null) return;
Audience au = audiences.sender(Bukkit.getConsoleSender());
au.sendMessage(getComponentFromMiniMessage(text));
}
@Override
public void sendPlayerMessage(Player player, String text) {
if (player == null) return;
Audience au = audiences.player(player);
au.sendMessage(getComponentFromMiniMessage(text));
}
@Override
public void sendActionbar(Player player, String text) {
if (player == null) return;
Audience au = audiences.player(player);
au.sendActionBar(getComponentFromMiniMessage(text));
}
@Override
public void sendSound(Player player, Sound.Source source, Key key, float pitch, float volume) {
if (player == null) return;
sendSound(player, Sound.sound(key, source, volume, pitch));
}
@Override
public void sendSound(Player player, Sound sound) {
if (player == null) return;
Audience au = audiences.player(player);
au.playSound(sound);
}
@Override
public void sendTitle(Player player, String title, String subTitle, int fadeIn, int stay, int fadeOut) {
if (player == null) return;
Audience au = audiences.player(player);
au.showTitle(Title.title(getComponentFromMiniMessage(title), getComponentFromMiniMessage(subTitle), Title.Times.times(
Duration.ofMillis(fadeIn * 50L),
Duration.ofMillis(stay * 50L),
Duration.ofMillis(fadeOut * 50L)
)));
}
@Override
public Component getComponentFromMiniMessage(String text) {
if (text == null) {
return Component.empty();
}
if (ConfigManager.legacyColorSupport()) {
return MiniMessage.miniMessage().deserialize(legacyToMiniMessage(text));
} else {
return MiniMessage.miniMessage().deserialize(text);
}
}
@Override
public String legacyToMiniMessage(String legacy) {
StringBuilder stringBuilder = new StringBuilder();
char[] chars = legacy.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (!isColorCode(chars[i])) {
stringBuilder.append(chars[i]);
continue;
}
if (i + 1 >= chars.length) {
stringBuilder.append(chars[i]);
continue;
}
switch (chars[i+1]) {
case '0' -> stringBuilder.append("<black>");
case '1' -> stringBuilder.append("<dark_blue>");
case '2' -> stringBuilder.append("<dark_green>");
case '3' -> stringBuilder.append("<dark_aqua>");
case '4' -> stringBuilder.append("<dark_red>");
case '5' -> stringBuilder.append("<dark_purple>");
case '6' -> stringBuilder.append("<gold>");
case '7' -> stringBuilder.append("<gray>");
case '8' -> stringBuilder.append("<dark_gray>");
case '9' -> stringBuilder.append("<blue>");
case 'a' -> stringBuilder.append("<green>");
case 'b' -> stringBuilder.append("<aqua>");
case 'c' -> stringBuilder.append("<red>");
case 'd' -> stringBuilder.append("<light_purple>");
case 'e' -> stringBuilder.append("<yellow>");
case 'f' -> stringBuilder.append("<white>");
case 'r' -> stringBuilder.append("<reset>");
case 'l' -> stringBuilder.append("<b>");
case 'm' -> stringBuilder.append("<st>");
case 'o' -> stringBuilder.append("<i>");
case 'n' -> stringBuilder.append("<u>");
case 'k' -> stringBuilder.append("<obf>");
case 'x' -> {
if (i + 13 >= chars.length
|| !isColorCode(chars[i+2])
|| !isColorCode(chars[i+4])
|| !isColorCode(chars[i+6])
|| !isColorCode(chars[i+8])
|| !isColorCode(chars[i+10])
|| !isColorCode(chars[i+12])) {
stringBuilder.append(chars[i]);
continue;
}
stringBuilder
.append("<#")
.append(chars[i+3])
.append(chars[i+5])
.append(chars[i+7])
.append(chars[i+9])
.append(chars[i+11])
.append(chars[i+13])
.append(">");
i += 12;
}
default -> {
stringBuilder.append(chars[i]);
continue;
}
}
i++;
}
return stringBuilder.toString();
}
@Override
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean isColorCode(char c) {
return c == '§' || c == '&';
}
@Override
public int rgbaToDecimal(String rgba) {
String[] split = rgba.split(",");
int r = Integer.parseInt(split[0]);
int g = Integer.parseInt(split[1]);
int b = Integer.parseInt(split[2]);
int a = Integer.parseInt(split[3]);
return (a << 24) | (r << 16) | (g << 8) | b;
}
}

View File

@@ -1,290 +0,0 @@
/*
* Copyright (C) <2022> <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.manager;
import dev.jorel.commandapi.*;
import dev.jorel.commandapi.arguments.ArgumentSuggestions;
import dev.jorel.commandapi.arguments.IntegerArgument;
import dev.jorel.commandapi.arguments.StringArgument;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.common.Initable;
import net.momirealms.customcrops.api.integration.SeasonInterface;
import net.momirealms.customcrops.api.manager.AdventureManager;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.manager.MessageManager;
import net.momirealms.customcrops.api.mechanic.item.ItemType;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsSection;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import net.momirealms.customcrops.compatibility.season.InBuiltSeason;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.generator.WorldInfo;
import java.util.Locale;
import java.util.Optional;
public class CommandManager implements Initable {
private final CustomCropsPlugin plugin;
public CommandManager(CustomCropsPlugin plugin) {
this.plugin = plugin;
init();
}
@Override
public void init() {
if (!CommandAPI.isLoaded())
CommandAPI.onLoad(new CommandAPIBukkitConfig(plugin).silentLogs(true));
new CommandAPICommand("customcrops")
.withPermission("customcrops.admin")
.withAliases("ccrops")
.withSubcommands(
getReloadCommand(),
getAboutCommand(),
getSeasonCommand(),
getDateCommand(),
getForceTickCommand(),
getUnsafeCommand()
)
.register();
}
@Override
public void disable() {
CommandAPI.unregister("customcrops");
}
private CommandAPICommand getReloadCommand() {
return new CommandAPICommand("reload")
.executes((sender, args) -> {
long time1 = System.currentTimeMillis();
plugin.reload();
long time2 = System.currentTimeMillis();
plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.reloadMessage().replace("{time}", String.valueOf(time2 - time1)));
});
}
private CommandAPICommand getUnsafeCommand() {
return new CommandAPICommand("unsafe")
.withSubcommands(
new CommandAPICommand("delete-chunk-data").executesPlayer((player, args) -> {
plugin.getWorldManager().getCustomCropsWorld(player.getWorld()).ifPresent(customCropsWorld -> {
var optionalChunk = customCropsWorld.getLoadedChunkAt(ChunkPos.getByBukkitChunk(player.getChunk()));
if (optionalChunk.isEmpty()) {
AdventureManager.getInstance().sendMessageWithPrefix(player, "<white>This chunk doesn't have any data.");
return;
}
customCropsWorld.deleteChunk(ChunkPos.getByBukkitChunk(player.getChunk()));
AdventureManager.getInstance().sendMessageWithPrefix(player, "<white>Done.");
});
}),
new CommandAPICommand("check-data").executesPlayer((player, args) -> {
Block block = player.getTargetBlockExact(10);
if (block != null) {
Optional<CustomCropsBlock> customCropsBlock = plugin.getWorldManager().getBlockAt(SimpleLocation.of(block.getLocation()));
if (customCropsBlock.isPresent()) {
AdventureManager.getInstance().sendMessageWithPrefix(player, customCropsBlock.get().getType() + ":" + customCropsBlock.get().getCompoundMap());
return;
}
}
AdventureManager.getInstance().sendMessageWithPrefix(player, "Data not found");
})
);
}
private CommandAPICommand getAboutCommand() {
return new CommandAPICommand("about").executes((sender, args) -> {
plugin.getAdventure().sendMessage(sender, "<#FFA500>⛈ CustomCrops <gray>- <#87CEEB>" + CustomCropsPlugin.getInstance().getVersionManager().getPluginVersion());
plugin.getAdventure().sendMessage(sender, "<#FFFFE0>Ultra-customizable planting experience for Minecraft servers");
plugin.getAdventure().sendMessage(sender, "<#DA70D6>\uD83E\uDDEA Author: <#FFC0CB>XiaoMoMi");
plugin.getAdventure().sendMessage(sender, "<#FF7F50>\uD83D\uDD25 Contributors: <#FFA07A>Cha_Shao<white>, <#FFA07A>TopOrigin<white>, <#FFA07A>AmazingCat");
plugin.getAdventure().sendMessage(sender, "<#FFD700>⭐ <click:open_url:https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customcrops>Document</click> <#A9A9A9>| <#FAFAD2>⛏ <click:open_url:https://github.com/Xiao-MoMi/Custom-Crops>Github</click> <#A9A9A9>| <#48D1CC>\uD83D\uDD14 <click:open_url:https://polymart.org/resource/customcrops.2625>Polymart</click>");
});
}
private CommandAPICommand getForceTickCommand() {
return new CommandAPICommand("force-tick")
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> Bukkit.getWorlds().stream().map(WorldInfo::getName).toList().toArray(new String[0]))))
.withArguments(new StringArgument("type").replaceSuggestions(ArgumentSuggestions.strings("sprinkler", "crop", "pot", "scarecrow", "greenhouse")))
.executes((sender, args) -> {
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
ItemType itemType = ItemType.valueOf(((String) args.get("type")).toUpperCase(Locale.ENGLISH));
Optional<CustomCropsWorld> customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world);
if (customCropsWorld.isEmpty()) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
plugin.getScheduler().runTaskAsync(() -> {
for (CustomCropsChunk chunk : customCropsWorld.get().getChunkStorage()) {
for (CustomCropsSection section : chunk.getSections()) {
for (CustomCropsBlock block : section.getBlocks()) {
if (block.getType() == itemType) {
block.tick(1, false);
}
}
}
}
});
});
}
private CommandAPICommand getDateCommand() {
return new CommandAPICommand("date")
.withSubcommands(
new CommandAPICommand("get")
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> plugin.getWorldManager().getCustomCropsWorlds().stream()
.filter(customCropsWorld -> customCropsWorld.getWorldSetting().isEnableSeason())
.map(CustomCropsWorld::getWorldName)
.toList()
.toArray(new String[0]))))
.executes((sender, args) -> {
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
plugin.getAdventure().sendMessageWithPrefix(sender, String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(world)));
}),
new CommandAPICommand("set")
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> plugin.getWorldManager().getCustomCropsWorlds().stream()
.filter(customCropsWorld -> customCropsWorld.getWorldSetting().isEnableSeason())
.map(CustomCropsWorld::getWorldName)
.toList()
.toArray(new String[0]))))
.withArguments(new IntegerArgument("date",1))
.executes((sender, args) -> {
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
int date = (int) args.getOrDefault("date", 1);
SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface();
if (!(seasonInterface instanceof InBuiltSeason inBuiltSeason)) {
plugin.getAdventure().sendMessageWithPrefix(sender, "Detected that you are using a season plugin. Please set date in that plugin.");
return;
}
Optional<CustomCropsWorld> customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world);
if (customCropsWorld.isEmpty()) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
if (!customCropsWorld.get().getWorldSetting().isEnableSeason()) {
plugin.getAdventure().sendMessageWithPrefix(sender, "Season is not enabled in that world");
return;
}
if (date > customCropsWorld.get().getWorldSetting().getSeasonDuration()) {
plugin.getAdventure().sendMessageWithPrefix(sender, "Date should be a value no higher than season duration");
return;
}
String pre = String.valueOf(inBuiltSeason.getDate(world));
customCropsWorld.get().getInfoData().setDate(date);
plugin.getAdventure().sendMessageWithPrefix(sender, "Date in world("+world.getName()+"): " + pre + " -> " + date);
})
);
}
private CommandAPICommand getSeasonCommand() {
return new CommandAPICommand("season")
.withSubcommands(
new CommandAPICommand("get")
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> plugin.getWorldManager().getCustomCropsWorlds().stream()
.filter(customCropsWorld -> customCropsWorld.getWorldSetting().isEnableSeason())
.map(CustomCropsWorld::getWorldName)
.toList()
.toArray(new String[0]))))
.executes((sender, args) -> {
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeasonInterface().getSeason(world)));
}),
new CommandAPICommand("set")
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> {
if (ConfigManager.syncSeasons()) {
return new String[]{ConfigManager.referenceWorld().getName()};
}
return plugin.getWorldManager().getCustomCropsWorlds().stream()
.filter(customCropsWorld -> customCropsWorld.getWorldSetting().isEnableSeason())
.map(CustomCropsWorld::getWorldName)
.toList()
.toArray(new String[0]);
})))
.withArguments(new StringArgument("season")
.replaceSuggestions(ArgumentSuggestions.stringsWithTooltips(info ->
new IStringTooltip[] {
StringTooltip.ofString("Spring", MessageManager.seasonTranslation(Season.SPRING)),
StringTooltip.ofString("Summer", MessageManager.seasonTranslation(Season.SUMMER)),
StringTooltip.ofString("Autumn", MessageManager.seasonTranslation(Season.AUTUMN)),
StringTooltip.ofString("Winter", MessageManager.seasonTranslation(Season.WINTER))
}
))
)
.executes((sender, args) -> {
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
String seasonName = (String) args.get("season");
SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface();
if (!(seasonInterface instanceof InBuiltSeason inBuiltSeason)) {
plugin.getAdventure().sendMessageWithPrefix(sender, "Detected that you are using a season plugin. Please set season in that plugin.");
return;
}
String pre = MessageManager.seasonTranslation(inBuiltSeason.getSeason(world));
Optional<CustomCropsWorld> customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world);
if (customCropsWorld.isEmpty()) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
if (!customCropsWorld.get().getWorldSetting().isEnableSeason()) {
plugin.getAdventure().sendMessageWithPrefix(sender, "Season is not enabled in that world");
return;
}
try {
Season season = Season.valueOf(seasonName.toUpperCase(Locale.ENGLISH));
customCropsWorld.get().getInfoData().setSeason(season);
String next = MessageManager.seasonTranslation(season);
plugin.getAdventure().sendMessageWithPrefix(sender, "Season in world("+world.getName()+"): " + pre + " -> " + next);
} catch (IllegalArgumentException e) {
plugin.getAdventure().sendMessageWithPrefix(sender, "That season doesn't exist");
}
})
);
}
}

View File

@@ -1,277 +0,0 @@
/*
* Copyright (C) <2022> <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.manager;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning;
import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.mechanic.item.ItemCarrier;
import net.momirealms.customcrops.api.util.LogUtils;
import net.momirealms.customcrops.util.ConfigUtils;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Locale;
import java.util.Objects;
public class ConfigManagerImpl extends ConfigManager {
public static final String configVersion = "37";
private final CustomCropsPlugin plugin;
private String lang;
private int maximumPoolSize;
private int corePoolSize;
private int keepAliveTime;
private boolean debug;
private boolean metrics;
private boolean legacyColorSupport;
private boolean protectLore;
private String[] itemDetectionOrder = new String[0];
private boolean checkUpdate;
private boolean disableMoisture;
private boolean preventTrampling;
private boolean greenhouse;
private boolean scarecrow;
private double[] defaultQualityRatio;
private String greenhouseID;
private String scarecrowID;
private int greenhouseRange;
private int scarecrowRange;
private boolean syncSeasons;
private WeakReference<World> referenceWorld;
private boolean convertWorldOnLoad;
private boolean scarecrowProtectChunk;
private ItemCarrier scarecrowItemType;
private ItemCarrier glassItemType;
public ConfigManagerImpl(CustomCropsPlugin plugin) {
this.plugin = plugin;
}
@Override
public void load() {
if (!new File(plugin.getDataFolder(), "config.yml").exists())
ConfigUtils.getConfig("config.yml");
// update config version
try {
YamlDocument.create(
new File(CustomCropsPlugin.getInstance().getDataFolder(), "config.yml"),
Objects.requireNonNull(CustomCropsPlugin.getInstance().getResource("config.yml")),
GeneralSettings.DEFAULT,
LoaderSettings
.builder()
.setAutoUpdate(true)
.build(),
DumperSettings.DEFAULT,
UpdaterSettings
.builder()
.setVersioning(new BasicVersioning("config-version"))
.addIgnoredRoute(configVersion, "other-settings.placeholder-register", '.')
.build()
);
} catch (IOException e) {
LogUtils.warn(e.getMessage());
}
YamlConfiguration config = ConfigUtils.getConfig("config.yml");
debug = config.getBoolean("debug");
metrics = config.getBoolean("metrics");
lang = config.getString("lang");
checkUpdate = config.getBoolean("update-checker", true);
ConfigurationSection otherSettings = config.getConfigurationSection("other-settings");
if (otherSettings == null) {
LogUtils.severe("other-settings section should not be null");
return;
}
maximumPoolSize = otherSettings.getInt("thread-pool-settings.maximumPoolSize", 10);
corePoolSize = otherSettings.getInt("thread-pool-settings.corePoolSize", 10);
keepAliveTime = otherSettings.getInt("thread-pool-settings.keepAliveTime", 30);
itemDetectionOrder = otherSettings.getStringList("item-detection-order").toArray(new String[0]);
protectLore = otherSettings.getBoolean("protect-original-lore", false);
legacyColorSupport = otherSettings.getBoolean("legacy-color-code-support", true);
convertWorldOnLoad = otherSettings.getBoolean("convert-on-world-load", false);
ConfigurationSection mechanics = config.getConfigurationSection("mechanics");
if (mechanics == null) {
LogUtils.severe("mechanics section should not be null");
return;
}
defaultQualityRatio = ConfigUtils.getQualityRatio(mechanics.getString("default-quality-ratio", "17/2/1"));
disableMoisture = mechanics.getBoolean("vanilla-farmland.disable-moisture-mechanic", false);
preventTrampling = mechanics.getBoolean("vanilla-farmland.prevent-trampling", false);
greenhouse = mechanics.getBoolean("greenhouse.enable", true);
greenhouseID = mechanics.getString("greenhouse.id");
greenhouseRange = mechanics.getInt("greenhouse.range", 5);
glassItemType = ItemCarrier.valueOf(mechanics.getString("greenhouse.type", "CHORUS").toUpperCase(Locale.ENGLISH));
scarecrow = mechanics.getBoolean("scarecrow.enable", true);
scarecrowID = mechanics.getString("scarecrow.id");
scarecrowRange = mechanics.getInt("scarecrow.range", 7);
scarecrowProtectChunk = mechanics.getBoolean("scarecrow.protect-chunk", false);
scarecrowItemType = ItemCarrier.valueOf(mechanics.getString("scarecrow.type", "ITEM_FRAME").toUpperCase(Locale.ENGLISH));
syncSeasons = mechanics.getBoolean("sync-season.enable", true);
if (syncSeasons) {
referenceWorld = new WeakReference<>(Bukkit.getWorld(mechanics.getString("sync-season.reference", "world")));
}
}
@Override
public void unload() {
}
@Override
public boolean hasLegacyColorSupport() {
return legacyColorSupport;
}
@Override
public int getMaximumPoolSize() {
return maximumPoolSize;
}
@Override
public int getCorePoolSize() {
return corePoolSize;
}
@Override
public int getKeepAliveTime() {
return keepAliveTime;
}
@Override
public boolean isConvertWorldOnLoad() {
return convertWorldOnLoad;
}
@Override
public double[] getDefaultQualityRatio() {
return defaultQualityRatio;
}
@Override
public String getLang() {
return lang;
}
@Override
public boolean getDebugMode() {
return debug;
}
@Override
public boolean isProtectLore() {
return protectLore;
}
@Override
public String[] getItemDetectionOrder() {
return itemDetectionOrder;
}
@Override
public boolean hasMetrics() {
return metrics;
}
@Override
public boolean hasCheckUpdate() {
return checkUpdate;
}
@Override
public boolean isDisableMoisture() {
return disableMoisture;
}
@Override
public boolean isPreventTrampling() {
return preventTrampling;
}
@Override
public boolean isGreenhouseEnabled() {
return greenhouse;
}
@Override
public String getGreenhouseID() {
return greenhouseID;
}
@Override
public int getGreenhouseRange() {
return greenhouseRange;
}
@Override
public boolean isScarecrowEnabled() {
return scarecrow;
}
@Override
public String getScarecrowID() {
return scarecrowID;
}
@Override
public int getScarecrowRange() {
return scarecrowRange;
}
@Override
public boolean isSyncSeasons() {
return syncSeasons;
}
@Override
public boolean doesScarecrowProtectChunk() {
return scarecrowProtectChunk;
}
@Override
public ItemCarrier getScarecrowItemCarrier() {
return scarecrowItemType;
}
@Override
public ItemCarrier getGlassItemCarrier() {
return glassItemType;
}
@Override
public World getReferenceWorld() {
return referenceWorld.get();
}
}

View File

@@ -1,182 +0,0 @@
/*
* Copyright (C) <2022> <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.manager;
import net.kyori.adventure.text.Component;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.common.Reloadable;
import net.momirealms.customcrops.api.common.Tuple;
import net.momirealms.customcrops.api.manager.VersionManager;
import net.momirealms.customcrops.api.scheduler.CancellableTask;
import net.momirealms.customcrops.util.FakeEntityUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
public class HologramManager implements Listener, Reloadable {
private final ConcurrentHashMap<UUID, HologramCache> hologramMap;
private final CustomCropsPlugin plugin;
private CancellableTask cacheCheckTask;
private static HologramManager manager;
public static HologramManager getInstance() {
return manager;
}
public HologramManager(CustomCropsPlugin plugin) {
this.plugin = plugin;
this.hologramMap = new ConcurrentHashMap<>();
manager = this;
}
@Override
public void load() {
Bukkit.getPluginManager().registerEvents(this, plugin);
this.cacheCheckTask = plugin.getScheduler().runTaskAsyncTimer(() -> {
ArrayList<UUID> removed = new ArrayList<>();
long current = System.currentTimeMillis();
for (Map.Entry<UUID, HologramCache> entry : hologramMap.entrySet()) {
Player player = Bukkit.getPlayer(entry.getKey());
if (player == null || !player.isOnline()) {
removed.add(entry.getKey());
} else {
entry.getValue().removeOutDated(current, player);
}
}
for (UUID uuid : removed) {
hologramMap.remove(uuid);
}
}, 200, 200, TimeUnit.MILLISECONDS);
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
for (Map.Entry<UUID, HologramCache> entry : hologramMap.entrySet()) {
Player player = Bukkit.getPlayer(entry.getKey());
if (player != null && player.isOnline()) {
entry.getValue().removeAll(player);
}
}
if (cacheCheckTask != null) cacheCheckTask.cancel();
this.hologramMap.clear();
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
this.hologramMap.remove(event.getPlayer().getUniqueId());
}
public void showHologram(Player player, Location location, Component component, int millis) {
HologramCache hologramCache = hologramMap.get(player.getUniqueId());
if (hologramCache != null) {
hologramCache.showHologram(player, location, component, millis);
} else {
hologramCache = new HologramCache();
hologramCache.showHologram(player, location, component, millis);
hologramMap.put(player.getUniqueId(), hologramCache);
}
}
@SuppressWarnings("unchecked")
public static class HologramCache {
private final Vector<Tuple<Location, Integer, Long>> tupleList;
private Tuple<Location, Integer, Long>[] tuples;
public HologramCache() {
this.tupleList = new Vector<>();
this.tuples = new Tuple[0];
}
public int push(Location new_loc, int time) {
for (Tuple<Location, Integer, Long> tuple : tuples) {
if (new_loc.equals(tuple.getLeft())) {
tuple.setRight(System.currentTimeMillis() + time);
return tuple.getMid();
}
}
return 0;
}
public void removeOutDated(long current, Player player) {
for (Tuple<Location, Integer, Long> tuple : tuples) {
if (tuple.getRight() < current) {
tupleList.remove(tuple);
this.tuples = tupleList.toArray(new Tuple[0]);
PacketManager.getInstance().send(player, FakeEntityUtils.getDestroyPacket(tuple.getMid()));
}
}
}
public void showHologram(Player player, Location location, Component component, int millis) {
int entity_id = push(location, millis);
if (entity_id == 0) {
int random = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
tupleList.add(Tuple.of(location, random, System.currentTimeMillis() + millis));
this.tuples = tupleList.toArray(new Tuple[0]);
if (VersionManager.isHigherThan1_20_R2()) {
PacketManager.getInstance().send(player,
FakeEntityUtils.getSpawnPacket(random, location.clone().add(0,1.25,0), EntityType.TEXT_DISPLAY),
FakeEntityUtils.get1_20_2TextDisplayMetaPacket(random, component)
);
} else if (VersionManager.isHigherThan1_19_R3()) {
PacketManager.getInstance().send(player,
FakeEntityUtils.getSpawnPacket(random, location.clone().add(0,1.25,0), EntityType.TEXT_DISPLAY),
FakeEntityUtils.get1_19_4TextDisplayMetaPacket(random, component)
);
} else {
PacketManager.getInstance().send(player,
FakeEntityUtils.getSpawnPacket(random, location, EntityType.ARMOR_STAND),
FakeEntityUtils.getVanishArmorStandMetaPacket(random, component)
);
}
} else {
if (VersionManager.isHigherThan1_20_R2()) {
PacketManager.getInstance().send(player, FakeEntityUtils.get1_20_2TextDisplayMetaPacket(entity_id, component));
} else if (VersionManager.isHigherThan1_19_R3()) {
PacketManager.getInstance().send(player, FakeEntityUtils.get1_19_4TextDisplayMetaPacket(entity_id, component));
} else {
PacketManager.getInstance().send(player, FakeEntityUtils.getVanishArmorStandMetaPacket(entity_id, component));
}
}
}
public void removeAll(Player player) {
for (Tuple<Location, Integer, Long> tuple : tuples) {
PacketManager.getInstance().send(player, FakeEntityUtils.getDestroyPacket(tuple.getMid()));
}
this.tupleList.clear();
this.tuples = null;
}
}
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright (C) <2022> <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.manager;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.common.Reloadable;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.manager.MessageManager;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import net.momirealms.customcrops.api.util.LogUtils;
import net.momirealms.customcrops.util.ConfigUtils;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
public class MessageManagerImpl extends MessageManager implements Reloadable {
private CustomCropsPlugin plugin;
private String reload;
private String prefix;
private String spring;
private String summer;
private String autumn;
private String winter;
private String noSeason;
public MessageManagerImpl(CustomCropsPlugin plugin) {
this.plugin = plugin;
}
@Override
public void load() {
YamlConfiguration config;
try {
config = ConfigUtils.getConfig("messages" + File.separator + ConfigManager.lang() + ".yml");
} catch (Exception e) {
LogUtils.warn(ConfigManager.lang() + ".yml doesn't exist. Using the default language file now.");
config = ConfigUtils.getConfig("messages" + File.separator + "en" + ".yml");
}
ConfigurationSection section = config.getConfigurationSection("messages");
if (section != null) {
prefix = section.getString("prefix", "<gradient:#ff206c:#fdee55>[CustomCrops]</gradient> ");
reload = section.getString("reload", "<white>Reloaded! Took <green>{time}ms.</green></white>");
spring = section.getString("spring", "Spring");
summer = section.getString("summer", "Summer");
autumn = section.getString("autumn", "Autumn");
winter = section.getString("winter", "Winter");
noSeason = section.getString("no-season", "Season Disabled");
}
}
@Override
public void unload() {
}
@Override
public String getSeasonTranslation(Season season) {
if (season == null) return noSeason;
return switch (season) {
case SPRING -> spring;
case SUMMER -> summer;
case AUTUMN -> autumn;
case WINTER -> winter;
};
}
@Override
public void reload() {
load();
}
@Override
public String getPrefix() {
return prefix;
}
@Override
protected String getReload() {
return reload;
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (C) <2022> <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.manager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class PacketManager {
private static PacketManager instance;
private final ProtocolManager protocolManager;
private final CustomCropsPlugin plugin;
public PacketManager(CustomCropsPlugin plugin) {
this.plugin = plugin;
this.protocolManager = ProtocolLibrary.getProtocolManager();
instance = this;
}
public static PacketManager getInstance() {
return instance;
}
public void send(Player player, PacketContainer packet) {
this.protocolManager.sendServerPacket(player, packet);
}
public void send(Player player, PacketContainer... packets) {
if (!player.isOnline()) return;
if (plugin.getVersionManager().isVersionNewerThan1_19_R3()) {
List<PacketContainer> bundle = new ArrayList<>(Arrays.asList(packets));
PacketContainer bundlePacket = new PacketContainer(PacketType.Play.Server.BUNDLE);
bundlePacket.getPacketBundles().write(0, bundle);
send(player, bundlePacket);
} else {
for (PacketContainer packet : packets) {
send(player, packet);
}
}
}
}

View File

@@ -1,120 +0,0 @@
/*
* Copyright (C) <2022> <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.manager;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.manager.PlaceholderManager;
import net.momirealms.customcrops.compatibility.papi.CCPapi;
import net.momirealms.customcrops.compatibility.papi.ParseUtils;
import net.momirealms.customcrops.util.ConfigUtils;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
public class PlaceholderManagerImpl extends PlaceholderManager {
private final HashMap<String, String> customPlaceholderMap;
private final CustomCropsPlugin plugin;
private final boolean hasPapi;
private CCPapi ccPapi;
public PlaceholderManagerImpl(CustomCropsPlugin plugin) {
this.plugin = plugin;
this.customPlaceholderMap = new HashMap<>();
this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
if (hasPapi) {
ccPapi = new CCPapi(plugin);
}
}
@Override
public void load() {
if (ccPapi != null) {
ccPapi.register();
}
this.loadCustomPlaceholders();
}
@Override
public void unload() {
if (ccPapi != null) {
ccPapi.unregister();
}
this.customPlaceholderMap.clear();
}
public void loadCustomPlaceholders() {
YamlConfiguration config = ConfigUtils.getConfig("config.yml");
ConfigurationSection section = config.getConfigurationSection("other-settings.placeholder-register");
if (section != null) {
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
this.customPlaceholderMap.put(entry.getKey(), (String) entry.getValue());
}
}
}
@Override
public String parse(@Nullable Player player, String text, Map<String, String> placeholders) {
var list = detectPlaceholders(text);
for (String papi : list) {
String replacer = null;
if (placeholders != null) {
replacer = placeholders.get(papi);
}
if (replacer == null) {
String custom = customPlaceholderMap.get(papi);
if (custom != null) {
replacer = setPlaceholders(player, parse(player, custom, placeholders));
}
}
if (replacer != null) {
text = text.replace(papi, replacer);
}
}
return text;
}
@Override
public List<String> parse(@Nullable Player player, List<String> list, Map<String, String> replacements) {
return list.stream()
.map(s -> parse(player, s, replacements))
.collect(Collectors.toList());
}
@Override
public List<String> detectPlaceholders(String text) {
List<String> placeholders = new ArrayList<>();
Matcher matcher = pattern.matcher(text);
while (matcher.find()) placeholders.add(matcher.group());
return placeholders;
}
@Override
public String setPlaceholders(Player player, String text) {
return hasPapi ? ParseUtils.setPlaceholders(player, text) : text;
}
}

View File

@@ -1,186 +0,0 @@
/*
* Copyright (C) <2022> <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.manager;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.manager.VersionManager;
import net.momirealms.customcrops.api.util.LogUtils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.CompletableFuture;
public class VersionManagerImpl extends VersionManager {
private final CustomCropsPlugin plugin;
private final String pluginVersion;
private boolean foliaScheduler;
private final boolean isSpigot;
private boolean isMojmap;
private final float mcVersion;
@SuppressWarnings("deprecation")
public VersionManagerImpl(CustomCropsPlugin plugin) {
this.plugin = plugin;
this.isSpigot = plugin.getServer().getName().equals("CraftBukkit");
this.pluginVersion = plugin.getDescription().getVersion();
String[] split = plugin.getServerVersion().split("\\.");
this.mcVersion = Float.parseFloat(split[1] + "." + (split.length >= 3 ? split[2] : "0"));
try {
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
this.foliaScheduler = true;
} catch (ClassNotFoundException ignored) {
this.foliaScheduler = false;
}
// Check if the server is Mojmap
try {
Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket");
this.isMojmap = true;
} catch (ClassNotFoundException ignored) {
}
}
@Override
public boolean hasRegionScheduler() {
return foliaScheduler;
}
@Override
public String getPluginVersion() {
return pluginVersion;
}
@Override
public boolean isSpigot() {
return isSpigot;
}
@Override
public boolean isVersionNewerThan1_19_R3() {
return mcVersion >= 19.4;
}
@Override
public boolean isVersionNewerThan1_20_R2() {
return mcVersion >= 20.2;
}
@Override
public boolean isVersionNewerThan1_19() {
return mcVersion >= 19;
}
@Override
public boolean isVersionNewerThan1_19_R2() {
return mcVersion >= 19.3;
}
@Override
public boolean isVersionNewerThan1_20() {
return mcVersion >= 20;
}
@Override
public boolean isVersionNewerThan1_18() {
return mcVersion >= 18;
}
@Override
public boolean isMojmap() {
return isMojmap;
}
@Override
public CompletableFuture<Boolean> checkUpdate() {
CompletableFuture<Boolean> updateFuture = new CompletableFuture<>();
plugin.getScheduler().runTaskAsync(() -> {
try {
URL url = new URL("https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=2625&key=version");
URLConnection conn = url.openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(60000);
InputStream inputStream = conn.getInputStream();
String newest = new BufferedReader(new InputStreamReader(inputStream)).readLine();
String current = plugin.getVersionManager().getPluginVersion();
inputStream.close();
if (!compareVer(newest, current)) {
updateFuture.complete(false);
return;
}
updateFuture.complete(true);
} catch (Exception exception) {
LogUtils.warn("Error occurred when checking update.", exception);
updateFuture.complete(false);
}
});
return updateFuture;
}
private boolean compareVer(String newV, String currentV) {
if (newV == null || currentV == null || newV.isEmpty() || currentV.isEmpty()) {
return false;
}
String[] newVS = newV.split("\\.");
String[] currentVS = currentV.split("\\.");
int maxL = Math.min(newVS.length, currentVS.length);
for (int i = 0; i < maxL; i++) {
try {
String[] newPart = newVS[i].split("-");
String[] currentPart = currentVS[i].split("-");
int newNum = Integer.parseInt(newPart[0]);
int currentNum = Integer.parseInt(currentPart[0]);
if (newNum > currentNum) {
return true;
} else if (newNum < currentNum) {
return false;
} else if (newPart.length > 1 && currentPart.length > 1) {
String[] newHotfix = newPart[1].split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
String[] currentHotfix = currentPart[1].split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
if (newHotfix.length == 2 && currentHotfix.length == 1) return true;
else if (newHotfix.length > 1 && currentHotfix.length > 1) {
int newHotfixNum = Integer.parseInt(newHotfix[1]);
int currentHotfixNum = Integer.parseInt(currentHotfix[1]);
if (newHotfixNum > currentHotfixNum) {
return true;
} else if (newHotfixNum < currentHotfixNum) {
return false;
} else {
return newHotfix[0].compareTo(currentHotfix[0]) > 0;
}
}
} else if (newPart.length > 1) {
return true;
} else if (currentPart.length > 1) {
return false;
}
}
catch (NumberFormatException ignored) {
return false;
}
}
return newVS.length > currentVS.length;
}
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.action;
import net.momirealms.customcrops.api.mechanic.action.Action;
import net.momirealms.customcrops.api.mechanic.requirement.State;
public class EmptyAction implements Action {
public static EmptyAction instance = new EmptyAction();
@Override
public void trigger(State state) {
}
}

View File

@@ -1,760 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.condition;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.common.Pair;
import net.momirealms.customcrops.api.manager.ConditionManager;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.mechanic.condition.Condition;
import net.momirealms.customcrops.api.mechanic.condition.ConditionExpansion;
import net.momirealms.customcrops.api.mechanic.condition.ConditionFactory;
import net.momirealms.customcrops.api.mechanic.item.Fertilizer;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import net.momirealms.customcrops.api.mechanic.world.level.WorldPot;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import net.momirealms.customcrops.api.util.LogUtils;
import net.momirealms.customcrops.compatibility.papi.ParseUtils;
import net.momirealms.customcrops.mechanic.misc.CrowAttackAnimation;
import net.momirealms.customcrops.mechanic.world.block.MemoryCrop;
import net.momirealms.customcrops.util.ClassUtils;
import net.momirealms.customcrops.util.ConfigUtils;
import net.momirealms.sparrow.heart.SparrowHeart;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.Farmland;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
public class ConditionManagerImpl implements ConditionManager {
private final String EXPANSION_FOLDER = "expansions/condition";
private final HashMap<String, ConditionFactory> conditionBuilderMap;
private final CustomCropsPlugin plugin;
public ConditionManagerImpl(CustomCropsPlugin plugin) {
this.plugin = plugin;
this.conditionBuilderMap = new HashMap<>();
this.registerInbuiltConditions();
}
@Override
public void load() {
this.loadExpansions();
}
@Override
public void unload() {
}
@Override
public void disable() {
this.conditionBuilderMap.clear();
}
private void registerInbuiltConditions() {
this.registerSeasonCondition();
this.registerWaterCondition();
this.registerTemperatureCondition();
this.registerAndCondition();
this.registerOrCondition();
this.registerRandomCondition();
this.registerEqualsCondition();
this.registerNumberEqualCondition();
this.registerRegexCondition();
this.registerGreaterThanCondition();
this.registerLessThanCondition();
this.registerContainCondition();
this.registerStartWithCondition();
this.registerEndWithCondition();
this.registerInListCondition();
this.registerBiomeRequirement();
this.registerFertilizerCondition();
this.registerCrowAttackCondition();
this.registerPotCondition();
this.registerLightCondition();
this.registerPointCondition();
this.registerWorldRequirement();
}
@Override
public boolean registerCondition(String type, ConditionFactory conditionFactory) {
if (this.conditionBuilderMap.containsKey(type)) return false;
this.conditionBuilderMap.put(type, conditionFactory);
return true;
}
@Override
public boolean unregisterCondition(String type) {
return this.conditionBuilderMap.remove(type) != null;
}
@Override
public @NotNull Condition[] getConditions(ConfigurationSection section) {
ArrayList<Condition> conditions = new ArrayList<>();
if (section != null) {
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
if (entry.getValue() instanceof ConfigurationSection innerSection) {
String key = entry.getKey();
if (hasCondition(key)) {
conditions.add(getCondition(key, innerSection));
} else {
conditions.add(getCondition(section.getConfigurationSection(key)));
}
}
}
}
return conditions.toArray(new Condition[0]);
}
@Override
public Condition getCondition(ConfigurationSection section) {
if (section == null) {
LogUtils.warn("Condition section should not be null");
return EmptyCondition.instance;
}
return getCondition(section.getString("type"), section.get("value"));
}
@Override
public Condition getCondition(String key, Object args) {
if (key == null) {
LogUtils.warn("Condition type should not be null");
return EmptyCondition.instance;
}
ConditionFactory factory = getConditionFactory(key);
if (factory == null) {
LogUtils.warn("Condition type: " + key + " doesn't exist.");
return EmptyCondition.instance;
}
return factory.build(args);
}
@Nullable
@Override
public ConditionFactory getConditionFactory(String type) {
return conditionBuilderMap.get(type);
}
private void registerCrowAttackCondition() {
registerCondition("crow_attack", (args -> {
if (args instanceof ConfigurationSection section) {
String flyModel = section.getString("fly-model");
String standModel = section.getString("stand-model");
double chance = section.getDouble("chance");
return (block, offline) -> {
if (Math.random() > chance) return false;
SimpleLocation location = block.getLocation();
if (ConfigManager.enableScarecrow()) {
Optional<CustomCropsWorld> world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName());
if (world.isEmpty()) return false;
CustomCropsWorld customCropsWorld = world.get();
if (!ConfigManager.scarecrowProtectChunk()) {
int range = ConfigManager.scarecrowRange();
for (int i = -range; i <= range; i++) {
for (int j = -range; j <= range; j++) {
for (int k : new int[]{0,-1,1}) {
if (customCropsWorld.getScarecrowAt(location.copy().add(i, k, j)).isPresent()) {
return false;
}
}
}
}
} else {
if (customCropsWorld.doesChunkHaveScarecrow(location)) {
return false;
}
}
}
if (!offline)
new CrowAttackAnimation(location, flyModel, standModel).start();
return true;
};
} else {
LogUtils.warn("Wrong value format found at crow-attack condition.");
return EmptyCondition.instance;
}
}));
}
private void registerWorldRequirement() {
registerCondition("world", (args) -> {
HashSet<String> worlds = new HashSet<>(ConfigUtils.stringListArgs(args));
return (block, offline) -> worlds.contains(block.getLocation().getWorldName());
});
registerCondition("!world", (args) -> {
HashSet<String> worlds = new HashSet<>(ConfigUtils.stringListArgs(args));
return (block, offline) -> !worlds.contains(block.getLocation().getWorldName());
});
}
private void registerBiomeRequirement() {
registerCondition("biome", (args) -> {
HashSet<String> biomes = new HashSet<>(ConfigUtils.stringListArgs(args));
return (block, offline) -> {
String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(block.getLocation().getBukkitLocation());
return biomes.contains(currentBiome);
};
});
registerCondition("!biome", (args) -> {
HashSet<String> biomes = new HashSet<>(ConfigUtils.stringListArgs(args));
return (block, offline) -> {
String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(block.getLocation().getBukkitLocation());
return !biomes.contains(currentBiome);
};
});
}
private void registerRandomCondition() {
registerCondition("random", (args -> {
double value = ConfigUtils.getDoubleValue(args);
return (block, offline) -> Math.random() < value;
}));
}
private void registerPotCondition() {
registerCondition("pot", (args -> {
HashSet<String> pots = new HashSet<>(ConfigUtils.stringListArgs(args));
return (block, offline) -> {
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0));
return worldPot.filter(pot -> pots.contains(pot.getKey())).isPresent();
};
}));
registerCondition("!pot", (args -> {
HashSet<String> pots = new HashSet<>(ConfigUtils.stringListArgs(args));
return (block, offline) -> {
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0));
return worldPot.filter(pot -> !pots.contains(pot.getKey())).isPresent();
};
}));
}
private void registerFertilizerCondition() {
registerCondition("fertilizer", (args -> {
HashSet<String> fertilizer = new HashSet<>(ConfigUtils.stringListArgs(args));
return (block, offline) -> {
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0));
return worldPot.filter(pot -> {
Fertilizer fertilizerInstance = pot.getFertilizer();
if (fertilizerInstance == null) return false;
return fertilizer.contains(fertilizerInstance.getKey());
}).isPresent();
};
}));
registerCondition("fertilizer_type", (args -> {
HashSet<String> fertilizer = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(str -> str.toUpperCase(Locale.ENGLISH)).toList());
return (block, offline) -> {
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0));
return worldPot.filter(pot -> {
Fertilizer fertilizerInstance = pot.getFertilizer();
if (fertilizerInstance == null) return false;
return fertilizer.contains(fertilizerInstance.getFertilizerType().name());
}).isPresent();
};
}));
}
private void registerAndCondition() {
registerCondition("&&", (args -> {
if (args instanceof ConfigurationSection section) {
Condition[] conditions = getConditions(section);
return (block, offline) -> ConditionManager.isConditionMet(block, offline, conditions);
} else {
LogUtils.warn("Wrong value format found at && condition.");
return EmptyCondition.instance;
}
}));
}
private void registerOrCondition() {
registerCondition("||", (args -> {
if (args instanceof ConfigurationSection section) {
Condition[] conditions = getConditions(section);
return (block, offline) -> {
for (Condition condition : conditions) {
if (condition.isConditionMet(block, offline)) {
return true;
}
}
return false;
};
} else {
LogUtils.warn("Wrong value format found at || condition.");
return EmptyCondition.instance;
}
}));
}
private void registerTemperatureCondition() {
registerCondition("temperature", (args) -> {
List<Pair<Integer, Integer>> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList();
return (block, offline) -> {
SimpleLocation location = block.getLocation();
World world = location.getBukkitWorld();
if (world == null) return false;
double temp = world.getTemperature(location.getX(), location.getY(), location.getZ());
for (Pair<Integer, Integer> pair : tempPairs) {
if (temp >= pair.left() && temp <= pair.right()) {
return true;
}
}
return false;
};
});
}
@SuppressWarnings("DuplicatedCode")
private void registerGreaterThanCondition() {
registerCondition(">=", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return Double.parseDouble(p1) >= Double.parseDouble(p2);
};
} else {
LogUtils.warn("Wrong value format found at >= requirement.");
return EmptyCondition.instance;
}
});
registerCondition(">", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return Double.parseDouble(p1) > Double.parseDouble(p2);
};
} else {
LogUtils.warn("Wrong value format found at > requirement.");
return EmptyCondition.instance;
}
});
}
private void registerRegexCondition() {
registerCondition("regex", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("papi", "");
String v2 = section.getString("regex", "");
return (block, offline) -> ParseUtils.setPlaceholders(null, v1).matches(v2);
} else {
LogUtils.warn("Wrong value format found at regex requirement.");
return EmptyCondition.instance;
}
});
}
private void registerNumberEqualCondition() {
registerCondition("==", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return Double.parseDouble(p1) == Double.parseDouble(p2);
};
} else {
LogUtils.warn("Wrong value format found at !startsWith requirement.");
return EmptyCondition.instance;
}
});
registerCondition("!=", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return Double.parseDouble(p1) != Double.parseDouble(p2);
};
} else {
LogUtils.warn("Wrong value format found at !startsWith requirement.");
return EmptyCondition.instance;
}
});
}
@SuppressWarnings("DuplicatedCode")
private void registerLessThanCondition() {
registerCondition("<", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return Double.parseDouble(p1) < Double.parseDouble(p2);
};
} else {
LogUtils.warn("Wrong value format found at < requirement.");
return EmptyCondition.instance;
}
});
registerCondition("<=", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return Double.parseDouble(p1) <= Double.parseDouble(p2);
};
} else {
LogUtils.warn("Wrong value format found at <= requirement.");
return EmptyCondition.instance;
}
});
}
private void registerStartWithCondition() {
registerCondition("startsWith", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return p1.startsWith(p2);
};
} else {
LogUtils.warn("Wrong value format found at startsWith requirement.");
return EmptyCondition.instance;
}
});
registerCondition("!startsWith", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return !p1.startsWith(p2);
};
} else {
LogUtils.warn("Wrong value format found at !startsWith requirement.");
return EmptyCondition.instance;
}
});
}
private void registerEndWithCondition() {
registerCondition("endsWith", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return p1.endsWith(p2);
};
} else {
LogUtils.warn("Wrong value format found at endsWith requirement.");
return EmptyCondition.instance;
}
});
registerCondition("!endsWith", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return !p1.endsWith(p2);
};
} else {
LogUtils.warn("Wrong value format found at !endsWith requirement.");
return EmptyCondition.instance;
}
});
}
private void registerContainCondition() {
registerCondition("contains", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return p1.contains(p2);
};
} else {
LogUtils.warn("Wrong value format found at contains requirement.");
return EmptyCondition.instance;
}
});
registerCondition("!contains", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return !p1.contains(p2);
};
} else {
LogUtils.warn("Wrong value format found at !contains requirement.");
return EmptyCondition.instance;
}
});
}
private void registerInListCondition() {
registerCondition("in-list", (args) -> {
if (args instanceof ConfigurationSection section) {
String papi = section.getString("papi", "");
HashSet<String> values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values")));
return (block, offline) -> {
String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(null, papi) : papi;
return values.contains(p1);
};
} else {
LogUtils.warn("Wrong value format found at in-list requirement.");
return EmptyCondition.instance;
}
});
registerCondition("!in-list", (args) -> {
if (args instanceof ConfigurationSection section) {
String papi = section.getString("papi", "");
HashSet<String> values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values")));
return (block, offline) -> {
String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(null, papi) : papi;
return !values.contains(p1);
};
} else {
LogUtils.warn("Wrong value format found at in-list requirement.");
return EmptyCondition.instance;
}
});
}
private void registerEqualsCondition() {
registerCondition("equals", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return p1.equals(p2);
};
} else {
LogUtils.warn("Wrong value format found at equals requirement.");
return EmptyCondition.instance;
}
});
registerCondition("!equals", (args) -> {
if (args instanceof ConfigurationSection section) {
String v1 = section.getString("value1", "");
String v2 = section.getString("value2", "");
return (block, offline) -> {
String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1;
String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2;
return !p1.equals(p2);
};
} else {
LogUtils.warn("Wrong value format found at !equals requirement.");
return EmptyCondition.instance;
}
});
}
private void registerSeasonCondition() {
registerCondition("suitable_season", (args) -> {
HashSet<String> seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList());
return (block, offline) -> {
Season season = plugin.getIntegrationManager().getSeasonInterface().getSeason(block.getLocation().getBukkitWorld());
if (season == null) {
return true;
}
if (seasons.contains(season.name())) {
return true;
}
if (ConfigManager.enableGreenhouse()) {
SimpleLocation location = block.getLocation();
Optional<CustomCropsWorld> world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName());
if (world.isEmpty()) return false;
CustomCropsWorld customCropsWorld = world.get();
for (int i = 1, range = ConfigManager.greenhouseRange(); i <= range; i++) {
if (customCropsWorld.getGlassAt(location.copy().add(0,i,0)).isPresent()) {
return true;
}
}
}
return false;
};
});
registerCondition("unsuitable_season", (args) -> {
HashSet<String> seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList());
return (block, offline) -> {
Season season = plugin.getIntegrationManager().getSeasonInterface().getSeason(block.getLocation().getBukkitWorld());
if (season == null) {
return false;
}
if (seasons.contains(season.name())) {
if (ConfigManager.enableGreenhouse()) {
SimpleLocation location = block.getLocation();
Optional<CustomCropsWorld> world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName());
if (world.isEmpty()) return false;
CustomCropsWorld customCropsWorld = world.get();
for (int i = 1, range = ConfigManager.greenhouseRange(); i <= range; i++) {
if (customCropsWorld.getGlassAt(location.copy().add(0,i,0)).isPresent()) {
return false;
}
}
}
return true;
}
return false;
};
});
}
private void registerLightCondition() {
registerCondition("skylight_more_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
int light = block.getLocation().getBukkitLocation().getBlock().getLightFromSky();
return value > light;
};
});
registerCondition("skylight_less_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
int light = block.getLocation().getBukkitLocation().getBlock().getLightFromSky();
return value < light;
};
});
registerCondition("light_more_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
int light = block.getLocation().getBukkitLocation().getBlock().getLightLevel();
return value > light;
};
});
registerCondition("light_less_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
int light = block.getLocation().getBukkitLocation().getBlock().getLightLevel();
return value < light;
};
});
}
private void registerPointCondition() {
registerCondition("point_more_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
if (block instanceof MemoryCrop crop) {
return crop.getPoint() > value;
}
return false;
};
});
registerCondition("point_less_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
if (block instanceof MemoryCrop crop) {
return crop.getPoint() < value;
}
return false;
};
});
}
private void registerWaterCondition() {
registerCondition("water_more_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0));
return worldPot.filter(pot -> pot.getWater() > value).isPresent();
};
});
registerCondition("water_less_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0));
return worldPot.filter(pot -> pot.getWater() < value).isPresent();
};
});
registerCondition("moisture_more_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
Block underBlock = block.getLocation().copy().add(0,-1,0).getBukkitLocation().getBlock();
if (underBlock.getBlockData() instanceof Farmland farmland) {
return farmland.getMoisture() > value;
}
return false;
};
});
registerCondition("moisture_less_than", (args) -> {
int value = (int) args;
return (block, offline) -> {
Block underBlock = block.getLocation().copy().add(0,-1,0).getBukkitLocation().getBlock();
if (underBlock.getBlockData() instanceof Farmland farmland) {
return farmland.getMoisture() < value;
}
return false;
};
});
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private void loadExpansions() {
File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER);
if (!expansionFolder.exists())
expansionFolder.mkdirs();
List<Class<? extends ConditionExpansion>> classes = new ArrayList<>();
File[] expansionJars = expansionFolder.listFiles();
if (expansionJars == null) return;
for (File expansionJar : expansionJars) {
if (expansionJar.getName().endsWith(".jar")) {
try {
Class<? extends ConditionExpansion> expansionClass = ClassUtils.findClass(expansionJar, ConditionExpansion.class);
classes.add(expansionClass);
} catch (IOException | ClassNotFoundException e) {
LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e);
}
}
}
try {
for (Class<? extends ConditionExpansion> expansionClass : classes) {
ConditionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance();
unregisterCondition(expansion.getConditionType());
registerCondition(expansion.getConditionType(), expansion.getConditionFactory());
LogUtils.info("Loaded condition expansion: " + expansion.getConditionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor());
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
LogUtils.warn("Error occurred when creating expansion instance.", e);
}
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.condition;
import net.momirealms.customcrops.api.mechanic.condition.Condition;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
public class EmptyCondition implements Condition {
public static EmptyCondition instance = new EmptyCondition();
@Override
public boolean isConditionMet(CustomCropsBlock block, boolean offline) {
return true;
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item;
import net.momirealms.customcrops.api.common.item.EventItem;
import net.momirealms.customcrops.api.manager.ActionManager;
import net.momirealms.customcrops.api.mechanic.action.Action;
import net.momirealms.customcrops.api.mechanic.action.ActionTrigger;
import net.momirealms.customcrops.api.mechanic.requirement.State;
import java.util.HashMap;
public abstract class AbstractEventItem implements EventItem {
private final HashMap<ActionTrigger, Action[]> actionMap;
public AbstractEventItem(HashMap<ActionTrigger, Action[]> actionMap) {
this.actionMap = actionMap;
}
@Override
public void trigger(ActionTrigger actionTrigger, State state) {
Action[] actions = actionMap.get(actionTrigger);
if (actions != null) {
ActionManager.triggerActions(state, actions);
}
}
}

View File

@@ -1,49 +0,0 @@
///*
// * Copyright (C) <2022> <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.mechanic.item.custom.crucible;
//
//import net.momirealms.customcrops.mechanic.item.ItemManagerImpl;
//import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener;
//import org.bukkit.event.EventHandler;
//
//public class CrucibleListener extends AbstractCustomListener {
//
// public CrucibleListener(ItemManagerImpl itemManager) {
// super(itemManager);
// }
//
// @EventHandler (ignoreCancelled = true)
// public void onBreakCustomBlock() {
// }
//
// @EventHandler (ignoreCancelled = true)
// public void onPlaceCustomBlock() {
// }
//
// @EventHandler (ignoreCancelled = true)
// public void onPlaceFurniture() {
// }
//
// @EventHandler (ignoreCancelled = true)
// public void onBreakFurniture() {
// }
//
// @EventHandler (ignoreCancelled = true)
// public void onInteractFurniture() {
// }
//}

View File

@@ -1,124 +0,0 @@
///*
// * Copyright (C) <2022> <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.mechanic.item.custom.crucible;
//
//import io.lumine.mythic.bukkit.BukkitAdapter;
//import io.lumine.mythic.bukkit.adapters.BukkitEntity;
//import io.lumine.mythiccrucible.MythicCrucible;
//import io.lumine.mythiccrucible.items.CrucibleItem;
//import io.lumine.mythiccrucible.items.ItemManager;
//import io.lumine.mythiccrucible.items.blocks.CustomBlockItemContext;
//import io.lumine.mythiccrucible.items.blocks.CustomBlockManager;
//import io.lumine.mythiccrucible.items.furniture.Furniture;
//import io.lumine.mythiccrucible.items.furniture.FurnitureManager;
//import net.momirealms.customcrops.api.util.LogUtils;
//import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider;
//import org.bukkit.Location;
//import org.bukkit.Material;
//import org.bukkit.block.Block;
//import org.bukkit.block.BlockFace;
//import org.bukkit.entity.Entity;
//import org.bukkit.inventory.ItemStack;
//
//import java.util.Optional;
//
//public class CrucibleProvider implements CustomProvider {
//
// private final ItemManager itemManager;
// private final CustomBlockManager blockManager;
// private final FurnitureManager furnitureManager;
//
// public CrucibleProvider() {
// this.itemManager = MythicCrucible.inst().getItemManager();
// this.blockManager = itemManager.getCustomBlockManager();
// this.furnitureManager = itemManager.getFurnitureManager();
// }
//
// @Override
// public boolean removeBlock(Location location) {
// Block block = location.getBlock();
// if (block.getType() == Material.AIR) {
// return false;
// }
// Optional<CustomBlockItemContext> optional = blockManager.getBlockFromBlock(block);
// if (optional.isPresent()) {
// optional.get().remove(block, null, false);
// } else {
// block.setType(Material.AIR);
// }
// return true;
// }
//
// @Override
// public void placeCustomBlock(Location location, String id) {
// Optional<CrucibleItem> optionalCI = itemManager.getItem(id);
// if (optionalCI.isPresent()) {
// location.getBlock().setBlockData(optionalCI.get().getBlockData().getBlockData());
// } else {
// LogUtils.warn("Custom block(" + id +") doesn't exist in Crucible configs. Please double check if that block exists.");
// }
// }
//
// @Override
// public Entity placeFurniture(Location location, String id) {
// Optional<CrucibleItem> optionalCI = itemManager.getItem(id);
// if (optionalCI.isPresent()) {
// return optionalCI.get().getFurnitureData().placeFrame(location.getBlock(), BlockFace.UP, 0f, null);
// } else {
// LogUtils.warn("Furniture(" + id +") doesn't exist in Crucible configs. Please double check if that furniture exists.");
// return null;
// }
// }
//
// @Override
// public void removeFurniture(Entity entity) {
// Optional<Furniture> optional = furnitureManager.getFurniture(entity.getUniqueId());
// optional.ifPresent(furniture -> furniture.getFurnitureData().remove(furniture, null, false, false));
// }
//
// @Override
// public String getBlockID(Block block) {
// Optional<CustomBlockItemContext> optionalCB = blockManager.getBlockFromBlock(block);
// return optionalCB.map(customBlockItemContext -> customBlockItemContext.getCrucibleItem().getInternalName()).orElse(block.getType().name());
// }
//
// @Override
// public String getItemID(ItemStack itemStack) {
// return itemManager.getItem(itemStack).map(CrucibleItem::getInternalName).orElse(null);
// }
//
// @Override
// public ItemStack getItemStack(String id) {
// Optional<CrucibleItem> optionalCI = itemManager.getItem(id);
// return optionalCI.map(crucibleItem -> BukkitAdapter.adapt(crucibleItem.getMythicItem().generateItemStack(1))).orElse(null);
// }
//
// @Override
// public String getEntityID(Entity entity) {
// Optional<CrucibleItem> optionalCI = furnitureManager.getItemFromEntity(entity);
// if (optionalCI.isPresent()) {
// return optionalCI.get().getInternalName();
// }
// return entity.getType().name();
// }
//
// @Override
// public boolean isFurniture(Entity entity) {
// return furnitureManager.isFurniture(new BukkitEntity(entity));
// }
//}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.custom.itemsadder;
import dev.lone.itemsadder.api.CustomFurniture;
import dev.lone.itemsadder.api.Events.*;
import net.momirealms.customcrops.api.mechanic.item.custom.AbstractCustomListener;
import net.momirealms.customcrops.mechanic.item.ItemManagerImpl;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
public class ItemsAdderListener extends AbstractCustomListener {
public ItemsAdderListener(ItemManagerImpl itemManager) {
super(itemManager);
}
@EventHandler (ignoreCancelled = true)
public void onBreakCustomBlock(CustomBlockBreakEvent event) {
this.itemManager.handlePlayerBreakBlock(
event.getPlayer(),
event.getBlock(),
event.getNamespacedID(),
event
);
}
@EventHandler (ignoreCancelled = true)
public void onPlaceCustomBlock(CustomBlockPlaceEvent event) {
super.onPlaceBlock(
event.getPlayer(),
event.getBlock(),
event.getNamespacedID(),
event
);
}
@EventHandler (ignoreCancelled = true)
public void onPlaceFurniture(FurniturePlaceSuccessEvent event) {
Entity entity = event.getBukkitEntity();
if (entity == null) return;
// player would be null if furniture is placed with API
if (event.getPlayer() == null) return;
super.onPlaceFurniture(
event.getPlayer(),
entity.getLocation(),
event.getNamespacedID(),
null
);
}
@EventHandler (ignoreCancelled = true)
public void onBreakFurniture(FurnitureBreakEvent event) {
CustomFurniture customFurniture = event.getFurniture();
if (customFurniture == null) return;
Entity entity = customFurniture.getEntity();
if (entity == null) return;
super.onBreakFurniture(
event.getPlayer(),
entity.getLocation(),
event.getNamespacedID(),
event
);
}
@EventHandler (ignoreCancelled = true)
public void onInteractFurniture(FurnitureInteractEvent event) {
CustomFurniture customFurniture = event.getFurniture();
if (customFurniture == null) return;
Entity entity = customFurniture.getEntity();
if (entity == null) return;
super.onInteractFurniture(event.getPlayer(),
entity.getLocation(),
event.getNamespacedID(),
entity,
event
);
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.custom.itemsadder;
import dev.lone.itemsadder.api.CustomBlock;
import dev.lone.itemsadder.api.CustomFurniture;
import dev.lone.itemsadder.api.CustomStack;
import net.momirealms.customcrops.api.mechanic.item.custom.CustomProvider;
import net.momirealms.customcrops.api.util.LogUtils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
public class ItemsAdderProvider implements CustomProvider {
@Override
public boolean removeBlock(Location location) {
Block block = location.getBlock();
if (block.getType() == Material.AIR)
return false;
if (!CustomBlock.remove(location)) {
block.setType(Material.AIR);
}
return true;
}
@Override
public void placeCustomBlock(Location location, String id) {
CustomBlock block = CustomBlock.place(id, location);
if (block == null) {
LogUtils.warn("Detected that custom block(" + id + ") doesn't exist in ItemsAdder configs. Please double check if that block exists.");
}
}
@Override
public Entity placeFurniture(Location location, String id) {
try {
CustomFurniture furniture = CustomFurniture.spawnPreciseNonSolid(id, location);
if (furniture == null) return null;
return furniture.getEntity();
} catch (RuntimeException e) {
LogUtils.warn("Failed to place ItemsAdder furniture. If this is not a problem caused by furniture not existing, consider increasing max-furniture-vehicles-per-chunk in ItemsAdder config.yml.");
e.printStackTrace();
return null;
}
}
@Override
public void removeFurniture(Entity entity) {
CustomFurniture.remove(entity, false);
}
@Override
public String getBlockID(Block block) {
CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block);
if (customBlock == null) {
return block.getType().name();
}
return customBlock.getNamespacedID();
}
@Override
public String getItemID(ItemStack itemInHand) {
CustomStack customStack = CustomStack.byItemStack(itemInHand);
if (customStack == null) {
return null;
}
return customStack.getNamespacedID();
}
@Override
public ItemStack getItemStack(String id) {
if (id == null) return new ItemStack(Material.AIR);
CustomStack customStack = CustomStack.getInstance(id);
if (customStack == null) {
return null;
}
return customStack.getItemStack().clone();
}
@Override
public String getEntityID(Entity entity) {
CustomFurniture customFurniture = CustomFurniture.byAlreadySpawned(entity);
if (customFurniture == null) {
return entity.getType().name();
}
return customFurniture.getNamespacedID();
}
@Override
public boolean isFurniture(Entity entity) {
try {
return CustomFurniture.byAlreadySpawned(entity) != null;
} catch (Exception e) {
return false;
}
}
}

View File

@@ -1,104 +0,0 @@
package net.momirealms.customcrops.mechanic.item.factory;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import java.util.List;
import java.util.Optional;
public class AbstractItem<R, I> implements Item<I> {
private final CustomCropsPlugin plugin;
private final ItemFactory<?, R, I> factory;
private final R item;
AbstractItem(CustomCropsPlugin plugin, ItemFactory<?, R, I> factory, R item) {
this.plugin = plugin;
this.factory = factory;
this.item = item;
}
@Override
public Item<I> customModelData(Integer data) {
factory.customModelData(item, data);
return this;
}
@Override
public Optional<Integer> customModelData() {
return factory.customModelData(item);
}
@Override
public Item<I> damage(Integer data) {
factory.damage(item, data);
return this;
}
@Override
public Optional<Integer> damage() {
return factory.damage(item);
}
@Override
public Item<I> maxDamage(Integer data) {
factory.maxDamage(item, data);
return this;
}
@Override
public Optional<Integer> maxDamage() {
return factory.maxDamage(item);
}
@Override
public Item<I> lore(List<String> lore) {
factory.lore(item, lore);
return this;
}
@Override
public Optional<List<String>> lore() {
return factory.lore(item);
}
@Override
public Optional<Object> getTag(Object... path) {
return factory.getTag(item, path);
}
@Override
public Item<I> setTag(Object value, Object... path) {
factory.setTag(item, value, path);
return this;
}
@Override
public boolean hasTag(Object... path) {
return factory.hasTag(item, path);
}
@Override
public boolean removeTag(Object... path) {
return factory.removeTag(item, path);
}
@Override
public I getItem() {
return factory.getItem(item);
}
@Override
public I load() {
return factory.load(item);
}
@Override
public I loadCopy() {
return factory.loadCopy(item);
}
@Override
public void update() {
factory.update(item);
}
}

View File

@@ -1,17 +0,0 @@
package net.momirealms.customcrops.mechanic.item.factory;
import net.kyori.adventure.key.Key;
public class ComponentKeys {
public static final String CUSTOM_MODEL_DATA = Key.key("minecraft", "custom_model_data").asString();
public static final String MAX_DAMAGE = Key.key("minecraft", "max_damage").asString();
public static final String CUSTOM_NAME = Key.key("minecraft", "custom_name").asString();
public static final String LORE = Key.key("minecraft", "lore").asString();
public static final String DAMAGE = Key.key("minecraft", "damage").asString();
public static final String ENCHANTMENT_GLINT_OVERRIDE = Key.key("minecraft", "enchantment_glint_override").asString();
public static final String HIDE_TOOLTIP = Key.key("minecraft", "hide_tooltip").asString();
public static final String MAX_STACK_SIZE = Key.key("minecraft", "max_stack_size").asString();
public static final String PROFILE = Key.key("minecraft", "profile").asString();
public static final String UNBREAKABLE = Key.key("minecraft", "unbreakable").asString();
}

View File

@@ -1,39 +0,0 @@
package net.momirealms.customcrops.mechanic.item.factory;
import java.util.List;
import java.util.Optional;
public interface Item<I> {
Item<I> customModelData(Integer data);
Optional<Integer> customModelData();
Item<I> damage(Integer data);
Optional<Integer> damage();
Item<I> maxDamage(Integer data);
Optional<Integer> maxDamage();
Item<I> lore(List<String> lore);
Optional<List<String>> lore();
Optional<Object> getTag(Object... path);
Item<I> setTag(Object value, Object... path);
boolean hasTag(Object... path);
boolean removeTag(Object... path);
I getItem();
I load();
I loadCopy();
void update();
}

View File

@@ -1,53 +0,0 @@
package net.momirealms.customcrops.mechanic.item.factory;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
public abstract class ItemFactory<P extends CustomCropsPlugin, R, I> {
protected final P plugin;
protected ItemFactory(P plugin) {
this.plugin = plugin;
}
public Item<I> wrap(R item) {
Objects.requireNonNull(item, "item");
return new AbstractItem<>(this.plugin, this, item);
}
protected abstract Optional<Object> getTag(R item, Object... path);
protected abstract void setTag(R item, Object value, Object... path);
protected abstract boolean hasTag(R item, Object... path);
protected abstract boolean removeTag(R item, Object... path);
protected abstract void update(R item);
protected abstract I load(R item);
protected abstract I getItem(R item);
protected abstract I loadCopy(R item);
protected abstract Optional<Integer> customModelData(R item);
protected abstract void customModelData(R item, Integer data);
protected abstract Optional<List<String>> lore(R item);
protected abstract void lore(R item, List<String> lore);
protected abstract Optional<Integer> maxDamage(R item);
protected abstract void maxDamage(R item, Integer data);
protected abstract Optional<Integer> damage(R item);
protected abstract void damage(R item, Integer data);
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function;
import net.momirealms.customcrops.mechanic.item.function.wrapper.ConditionWrapper;
import org.jetbrains.annotations.NotNull;
import java.util.function.Function;
public class CFunction implements Comparable<CFunction> {
private static int functionID = 0;
private final Function<ConditionWrapper, FunctionResult> function;
private final FunctionPriority priority;
private final int id;
public CFunction(Function<ConditionWrapper, FunctionResult> function, FunctionPriority priority) {
this.function = function;
this.priority = priority;
this.id = functionID++;
}
public FunctionResult apply(ConditionWrapper wrapper) {
return function.apply(wrapper);
}
public Function<ConditionWrapper, FunctionResult> getFunction() {
return function;
}
public FunctionPriority getPriority() {
return priority;
}
@Override
public int compareTo(@NotNull CFunction o) {
if (this.priority.ordinal() > o.priority.ordinal()) {
return 1;
} else if (this.priority.ordinal() < o.priority.ordinal()) {
return -1;
}
return Integer.compare(this.id, o.id);
}
public static void resetID() {
functionID = 0;
}
public enum FunctionPriority {
HIGHEST,
HIGH,
NORMAL,
LOW,
LOWEST;
public boolean isHigherThan(FunctionPriority priority) {
return this.ordinal() < priority.ordinal();
}
}
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function;
public enum FunctionResult {
PASS,
RETURN,
CANCEL_EVENT_AND_RETURN
}

View File

@@ -1,26 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function;
public enum FunctionTrigger {
PLACE,
BREAK,
BE_INTERACTED,
INTERACT_AT,
INTERACT_AIR
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
public class BreakBlockWrapper extends BreakWrapper {
private final Block brokenBlock;
public BreakBlockWrapper(Player player, Block brokenBlock) {
super(player, brokenBlock.getLocation());
this.brokenBlock = brokenBlock;
}
public Block getBrokenBlock() {
return brokenBlock;
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class BreakFurnitureWrapper extends BreakWrapper {
private final Location location;
private final String id;
public BreakFurnitureWrapper(Player player, Location location, String id) {
super(player, location);
this.location = location;
this.id = id;
}
@NotNull
public Location getLocation() {
return location;
}
@NotNull
public String getID() {
return id;
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.Location;
import org.bukkit.entity.Player;
public class BreakWrapper extends ConditionWrapper {
private final Location location;
public BreakWrapper(Player player, Location location) {
super(player);
this.location = location;
}
public Location getLocation() {
return location;
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public abstract class ConditionWrapper {
private final Player player;
private final ItemStack itemInHand;
public ConditionWrapper(Player player) {
this.player = player;
this.itemInHand = player.getInventory().getItemInMainHand();
}
public Player getPlayer() {
return player;
}
public ItemStack getItemInHand() {
return itemInHand;
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
public class InteractBlockWrapper extends InteractWrapper {
private final Block clickedBlock;
private final BlockFace clickedFace;
public InteractBlockWrapper(Player player, Block clickedBlock, BlockFace clickedFace) {
super(player, clickedBlock.getLocation());
this.clickedBlock = clickedBlock;
this.clickedFace = clickedFace;
}
public Block getClickedBlock() {
return clickedBlock;
}
public BlockFace getClickedFace() {
return clickedFace;
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class InteractFurnitureWrapper extends InteractWrapper {
private final String id;
private final Entity entity;
public InteractFurnitureWrapper(Player player, Location location, String id, @Nullable Entity baseEntity) {
super(player, location);
this.entity = baseEntity;
this.id = id;
}
@Nullable
public Entity getEntity() {
return entity;
}
@NotNull
public String getID() {
return id;
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.Location;
import org.bukkit.entity.Player;
public class InteractWrapper extends ConditionWrapper {
private final Location location;
public InteractWrapper(Player player, Location location) {
super(player);
this.location = location;
}
public Location getLocation() {
return location;
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
public class PlaceBlockWrapper extends PlaceWrapper {
private final Block placedBlock;
public PlaceBlockWrapper(Player player, Block placedBlock, String blockID) {
super(player, placedBlock.getLocation(), blockID);
this.placedBlock = placedBlock;
}
public Block getPlacedBlock() {
return placedBlock;
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.Location;
import org.bukkit.entity.Player;
public class PlaceFurnitureWrapper extends PlaceWrapper {
public PlaceFurnitureWrapper(Player player, Location location, String id) {
super(player, location, id);
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.function.wrapper;
import org.bukkit.Location;
import org.bukkit.entity.Player;
public class PlaceWrapper extends ConditionWrapper {
private final Location location;
private final String id;
public PlaceWrapper(Player player, Location location, String id) {
super(player);
this.location = location;
this.id = id;
}
public Location getLocation() {
return location;
}
public String getId() {
return id;
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.impl;
import net.momirealms.customcrops.api.mechanic.action.Action;
import net.momirealms.customcrops.api.mechanic.action.ActionTrigger;
import net.momirealms.customcrops.api.mechanic.item.Fertilizer;
import net.momirealms.customcrops.api.mechanic.item.FertilizerType;
import net.momirealms.customcrops.api.mechanic.requirement.Requirement;
import net.momirealms.customcrops.mechanic.item.AbstractEventItem;
import java.util.HashMap;
import java.util.HashSet;
public class AbstractFertilizer extends AbstractEventItem implements Fertilizer {
private final String key;
private final String itemID;
private final int times;
private final FertilizerType fertilizerType;
private final HashSet<String> potWhitelist;
private final boolean beforePlant;
private final String icon;
private final Requirement[] requirements;
public AbstractFertilizer(
String key,
String itemID,
int times,
FertilizerType fertilizerType,
HashSet<String> potWhitelist,
boolean beforePlant,
String icon,
Requirement[] requirements,
HashMap<ActionTrigger, Action[]> actionMap
) {
super(actionMap);
this.key = key;
this.itemID = itemID;
this.times = times;
this.fertilizerType = fertilizerType;
this.potWhitelist = potWhitelist;
this.beforePlant = beforePlant;
this.icon = icon;
this.requirements = requirements;
}
@Override
public String getKey() {
return key;
}
@Override
public String getItemID() {
return itemID;
}
@Override
public int getTimes() {
return times;
}
@Override
public FertilizerType getFertilizerType() {
return fertilizerType;
}
@Override
public HashSet<String> getPotWhitelist() {
return potWhitelist;
}
@Override
public boolean isBeforePlant() {
return beforePlant;
}
@Override
public String getIcon() {
return icon;
}
@Override
public Requirement[] getRequirements() {
return requirements;
}
}

View File

@@ -1,241 +0,0 @@
/*
* Copyright (C) <2022> <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.mechanic.item.impl;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.mechanic.action.Action;
import net.momirealms.customcrops.api.mechanic.action.ActionTrigger;
import net.momirealms.customcrops.api.mechanic.condition.Conditions;
import net.momirealms.customcrops.api.mechanic.condition.DeathConditions;
import net.momirealms.customcrops.api.mechanic.item.BoneMeal;
import net.momirealms.customcrops.api.mechanic.item.Crop;
import net.momirealms.customcrops.api.mechanic.item.ItemCarrier;
import net.momirealms.customcrops.api.mechanic.requirement.Requirement;
import net.momirealms.customcrops.mechanic.item.AbstractEventItem;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
public class CropConfig extends AbstractEventItem implements Crop {
private final String key;
private final String seedID;
private final int maxPoints;
private final boolean rotation;
private final ItemCarrier carrier;
private final Requirement[] plantRequirements;
private final Requirement[] breakRequirements;
private final Requirement[] interactRequirements;
private final BoneMeal[] boneMeals;
private final Conditions growConditions;
private final HashSet<String> whitelistPots;
private final DeathConditions[] deathConditions;
private final HashMap<Integer, CropStageConfig> point2StageConfigMap;
private final HashMap<String, CropStageConfig> item2StageConfigMap;
public CropConfig(
String key,
String seedID,
ItemCarrier carrier,
HashSet<String> whitelistPots,
boolean rotation,
int maxPoints,
BoneMeal[] boneMeals,
Conditions growConditions,
DeathConditions[] deathConditions,
HashMap<ActionTrigger, Action[]> actionMap,
HashMap<Integer, CropStageConfig> point2StageConfigMap,
Requirement[] plantRequirements,
Requirement[] breakRequirements,
Requirement[] interactRequirements
) {
super(actionMap);
this.key = key;
this.seedID = seedID;
this.boneMeals = boneMeals;
this.rotation = rotation;
this.maxPoints = maxPoints;
this.growConditions = growConditions;
this.deathConditions = deathConditions;
this.plantRequirements = plantRequirements;
this.breakRequirements = breakRequirements;
this.interactRequirements = interactRequirements;
this.point2StageConfigMap = point2StageConfigMap;
this.whitelistPots = whitelistPots;
this.carrier = carrier;
this.item2StageConfigMap = new HashMap<>();
for (CropStageConfig cropStageConfig : point2StageConfigMap.values()) {
if (cropStageConfig.getStageID() != null) {
this.item2StageConfigMap.put(cropStageConfig.getStageID(), cropStageConfig);
}
}
}
@Override
public String getKey() {
return key;
}
@Override
public String getSeedItemID() {
return seedID;
}
@Override
public int getMaxPoints() {
return maxPoints;
}
@Override
public Requirement[] getPlantRequirements() {
return plantRequirements;
}
@Override
public Requirement[] getBreakRequirements() {
return breakRequirements;
}
@Override
public Requirement[] getInteractRequirements() {
return interactRequirements;
}
@Override
public Conditions getGrowConditions() {
return growConditions;
}
@Override
public DeathConditions[] getDeathConditions() {
return deathConditions;
}
@Override
public BoneMeal[] getBoneMeals() {
return boneMeals;
}
@Override
public boolean hasRotation() {
return rotation;
}
@Override
public Stage getStageByPoint(int point) {
return point2StageConfigMap.get(point);
}
@NotNull
@Override
public String getStageItemByPoint(int point) {
if (point >= 0) {
Stage stage = point2StageConfigMap.get(point);
if (stage != null) {
String id = stage.getStageID();
if (id == null) return getStageItemByPoint(point-1);
return id;
} else {
return getStageItemByPoint(point-1);
}
}
return null;
}
@Override
public Stage getStageByItemID(String itemID) {
return item2StageConfigMap.get(itemID);
}
@Override
public Collection<? extends Stage> getStages() {
return new ArrayList<>(point2StageConfigMap.values());
}
@Override
public HashSet<String> getPotWhitelist() {
return whitelistPots;
}
@Override
public ItemCarrier getItemCarrier() {
return carrier;
}
public static class CropStageConfig extends AbstractEventItem implements Crop.Stage {
@Nullable
private final String stageID;
private final int point;
private final double hologramOffset;
private final Requirement[] interactRequirements;
private final Requirement[] breakRequirements;
public CropStageConfig(
@Nullable String stageID,
int point,
double hologramOffset,
HashMap<ActionTrigger, Action[]> actionMap,
Requirement[] interactRequirements,
Requirement[] breakRequirements
) {
super(actionMap);
this.stageID = stageID;
this.point = point;
this.hologramOffset = hologramOffset;
this.interactRequirements = interactRequirements;
this.breakRequirements = breakRequirements;
}
@Override
public Crop getCrop() {
return CustomCropsPlugin.get().getItemManager().getCropByStageID(stageID);
}
@Override
public double getHologramOffset() {
return hologramOffset;
}
@Nullable
@Override
public String getStageID() {
return stageID;
}
@Override
public int getPoint() {
return point;
}
@Override
public Requirement[] getInteractRequirements() {
return interactRequirements;
}
@Override
public Requirement[] getBreakRequirements() {
return breakRequirements;
}
}
}

Some files were not shown because too many files have changed in this diff Show More