9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-22 08:29:35 +00:00

1.3.1 remake

This commit is contained in:
Xiao-MoMi
2022-05-29 15:52:41 +08:00
parent 67b0441f31
commit 592e4598df
28 changed files with 5113 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
package net.momirealms.customcrops;
import net.momirealms.customcrops.datamanager.BackUp;
import net.momirealms.customcrops.datamanager.CropManager;
import net.momirealms.customcrops.datamanager.NextSeason;
import net.momirealms.customcrops.datamanager.SprinklerManager;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import javax.annotation.ParametersAreNonnullByDefault;
public class CommandHandler implements CommandExecutor {
@Override
@ParametersAreNonnullByDefault
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if(sender instanceof Player && !sender.isOp()){
return false;
}
//重载插件
if(args[0].equalsIgnoreCase("reload")){
ConfigManager.Config.ReloadConfig();
if(sender instanceof Player){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.reload, (Player) sender);
}else {
MessageManager.consoleMessage(ConfigManager.Config.prefix + ConfigManager.Config.reload, Bukkit.getConsoleSender());
}
return true;
}
//设置季节
if(args[0].equalsIgnoreCase("setseason")){
if(ConfigManager.Config.season){
FileConfiguration config = CustomCrops.instance.getConfig();
config.set("current-season", args[1]);
CustomCrops.instance.saveConfig();
ConfigManager.Config.current = args[1];
if(sender instanceof Player){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.season_set.replace("{Season}",args[1])
.replace("spring", ConfigManager.Config.spring)
.replace("summer", ConfigManager.Config.summer)
.replace("autumn", ConfigManager.Config.autumn)
.replace("winter", ConfigManager.Config.winter), (Player) sender);
}else {
MessageManager.consoleMessage(config.getString("messages.prefix") + ConfigManager.Config.season_set.replace("{Season}",args[1])
.replace("spring", ConfigManager.Config.spring)
.replace("summer", ConfigManager.Config.summer)
.replace("autumn", ConfigManager.Config.autumn)
.replace("winter", ConfigManager.Config.winter), Bukkit.getConsoleSender());
}
}else{
if(sender instanceof Player){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.season_disabled, (Player) sender);
}else {
MessageManager.consoleMessage(ConfigManager.Config.prefix + ConfigManager.Config.season_disabled, Bukkit.getConsoleSender());
}
}
return true;
}
//强制保存
if(args[0].equalsIgnoreCase("forcesave")){
CropManager.saveData();
SprinklerManager.saveData();
if(sender instanceof Player){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.force_save, (Player) sender);
}else {
MessageManager.consoleMessage(ConfigManager.Config.prefix + ConfigManager.Config.force_save, Bukkit.getConsoleSender());
}
return true;
}
//强制生长
if(args[0].equalsIgnoreCase("forcegrow")){
Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.instance, CropManager::CropGrow);
if(sender instanceof Player){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.force_grow, (Player) sender);
}else {
MessageManager.consoleMessage(ConfigManager.Config.prefix + ConfigManager.Config.force_grow, Bukkit.getConsoleSender());
}
return true;
}
//强制洒水
if(args[0].equalsIgnoreCase("forcewater")){
Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.instance, SprinklerManager::SprinklerWork);
if(sender instanceof Player){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.force_water, (Player) sender);
}else {
MessageManager.consoleMessage(ConfigManager.Config.prefix + ConfigManager.Config.force_water, Bukkit.getConsoleSender());
}
return true;
}
if(args[0].equalsIgnoreCase("backup")){
BackUp.backUpData();
if(sender instanceof Player){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.backup, (Player) sender);
}else {
MessageManager.consoleMessage(ConfigManager.Config.prefix + ConfigManager.Config.backup, Bukkit.getConsoleSender());
}
return true;
}
if(args[0].equalsIgnoreCase("nextseason")){
NextSeason.changeSeason();
if(sender instanceof Player){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.nextSeason, (Player) sender);
}else {
MessageManager.consoleMessage(ConfigManager.Config.prefix + ConfigManager.Config.nextSeason, Bukkit.getConsoleSender());
}
return true;
}
if(args[0].equalsIgnoreCase("test")){
CropManager.testData_2();
SprinklerManager.testData_3();
return true;
}
return false;
}
}

View File

@@ -0,0 +1,24 @@
package net.momirealms.customcrops;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Arrays;
import java.util.List;
public class CommandTabComplete implements TabCompleter {
@Override
@ParametersAreNonnullByDefault
public @Nullable List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (args.length == 1) {
return Arrays.asList("backup" , "forcegrow", "forcesave", "forcewater", "reload", "setseason" , "nextseason");
}
if(args[0].equalsIgnoreCase("setseason")){
return Arrays.asList("spring","summer","autumn","winter");
}
return null;
}
}

View File

@@ -0,0 +1,134 @@
package net.momirealms.customcrops;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.List;
public class ConfigManager {
public static class Config{
public static boolean res;
public static boolean wg;
public static boolean king;
public static boolean season;
public static boolean need_water;
public static boolean greenhouse;
public static boolean big;
public static boolean limit;
public static List<String> worlds;
public static List<Long> cropGrowTimeList;
public static List<Long> sprinklerWorkTimeList;
public static String current;
public static String prefix;
public static String bad_place;
public static String reload;
public static String force_save;
public static String nextSeason;
public static String no_such_seed;
public static String wrong_season;
public static String season_set;
public static String season_disabled;
public static String force_grow;
public static String force_water;
public static String limit_crop;
public static String limit_sprinkler;
public static String backup;
public static String spring;
public static String summer;
public static String autumn;
public static String winter;
public static String can_full;
public static String pot;
public static String watered_pot;
public static String watering_can_1;
public static String watering_can_2;
public static String watering_can_3;
public static String glass;
public static String sprinkler_1;
public static String sprinkler_2;
public static String sprinkler_1i;
public static String sprinkler_2i;
public static String dead;
public static String success;
public static String failure;
public static double bone_chance;
public static double grow_chance;
public static double big_chance;
public static int range;
public static int maxh;
public static int minh;
public static int max_crop;
public static int max_sprinkler;
public static void ReloadConfig(){
CustomCrops.instance.reloadConfig();
FileConfiguration configuration = CustomCrops.instance.getConfig();
//处理配置
Config.res = configuration.getBoolean("integration.residence");
Config.king = configuration.getBoolean("integration.kingdomsX");
Config.wg = configuration.getBoolean("integration.worldguard");
Config.season = configuration.getBoolean("enable-season");
Config.need_water = configuration.getBoolean("config.bone-meal-consume-water");
Config.greenhouse = configuration.getBoolean("config.enable-greenhouse");
Config.big = configuration.getBoolean("config.gigantic.enable");
Config.limit = configuration.getBoolean("config.enable-limit");
Config.bone_chance = configuration.getDouble("config.bone-meal-chance");
Config.grow_chance = configuration.getDouble("config.grow-success-chance");
Config.big_chance = configuration.getDouble("config.gigantic.chance");
Config.range = configuration.getInt("config.greenhouse-range");
Config.maxh = configuration.getInt("config.height.max");
Config.minh = configuration.getInt("config.height.min");
Config.max_crop = configuration.getInt("config.max-crops");
Config.max_sprinkler = configuration.getInt("config.max-sprinklers");
Config.current = configuration.getString("current-season");
Config.pot = configuration.getString("config.pot");
Config.watered_pot = configuration.getString("config.watered-pot");
Config.watering_can_1 = configuration.getString("config.watering-can-1");
Config.watering_can_2 = configuration.getString("config.watering-can-2");
Config.watering_can_3 = configuration.getString("config.watering-can-3");
Config.glass = configuration.getString("config.greenhouse-glass");
Config.sprinkler_1 = configuration.getString("config.sprinkler-1");
Config.sprinkler_2 = configuration.getString("config.sprinkler-2");
Config.sprinkler_1i = configuration.getString("config.sprinkler-1-item");
Config.sprinkler_2i = configuration.getString("config.sprinkler-2-item");
Config.dead = configuration.getString("config.dead-crop");
Config.success = configuration.getString("config.particle.success");
Config.failure = configuration.getString("config.particle.failure");
Config.worlds = configuration.getStringList("config.whitelist-worlds");
Config.cropGrowTimeList = configuration.getLongList("config.grow-time");
Config.sprinklerWorkTimeList = configuration.getLongList("config.sprinkler-time");
//处理消息
Config.prefix = configuration.getString("messages.prefix");
Config.bad_place = configuration.getString("messages.not-a-good-place");
Config.reload = configuration.getString("messages.reload");
Config.force_save = configuration.getString("messages.force-save");
Config.nextSeason = configuration.getString("messages.nextseason");
Config.no_such_seed = configuration.getString("messages.no-such-seed");
Config.wrong_season = configuration.getString("messages.wrong-season");
Config.season_set = configuration.getString("messages.season-set");
Config.season_disabled = configuration.getString("messages.season-disabled");
Config.force_grow = configuration.getString("messages.force-grow");
Config.force_water = configuration.getString("messages.force-water");
Config.limit_crop = configuration.getString("messages.reach-limit-crop");
Config.limit_sprinkler = configuration.getString("messages.reach-limit-sprinkler");
Config.can_full = configuration.getString("messages.can-full");
Config.backup = configuration.getString("messages.backup");
Config.spring = configuration.getString("messages.spring");
Config.summer = configuration.getString("messages.summer");
Config.autumn = configuration.getString("messages.autumn");
Config.winter = configuration.getString("messages.winter");
}
}
}

View File

@@ -0,0 +1,101 @@
package net.momirealms.customcrops;
import com.comphenix.protocol.ProtocolManager;
import net.momirealms.customcrops.crops.CropTimer;
import net.momirealms.customcrops.datamanager.BackUp;
import net.momirealms.customcrops.datamanager.CropManager;
import net.momirealms.customcrops.datamanager.SprinklerManager;
import net.momirealms.customcrops.listener.BreakCustomBlock;
import net.momirealms.customcrops.listener.BreakFurniture;
import net.momirealms.customcrops.listener.RightClickBlock;
import net.momirealms.customcrops.listener.RightClickCustomBlock;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
public final class CustomCrops extends JavaPlugin {
public static JavaPlugin instance;
public static ProtocolManager manager;
public static CropTimer timer;
public static CropManager cropManager;
public static SprinklerManager sprinklerManager;
@Override
public void onEnable() {
instance = this;
saveDefaultConfig();
//加载配置文件
ConfigManager.Config.ReloadConfig();
//指令注册
Objects.requireNonNull(Bukkit.getPluginCommand("customcrops")).setExecutor(new CommandHandler());
Objects.requireNonNull(Bukkit.getPluginCommand("customcrops")).setTabCompleter(new CommandTabComplete());
Bukkit.getPluginManager().registerEvents(new RightClickCustomBlock(),this);
Bukkit.getPluginManager().registerEvents(new BreakCustomBlock(),this);
Bukkit.getPluginManager().registerEvents(new RightClickBlock(),this);
Bukkit.getPluginManager().registerEvents(new BreakFurniture(),this);
//开始计时任务
CustomCrops.timer = new CropTimer();
//新建data文件
File crop_file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml");
File sprinkler_file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml");
if(!crop_file.exists()){
try {
crop_file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
MessageManager.consoleMessage("&c[CustomCrops] 农作物数据文件生成失败!",Bukkit.getConsoleSender());
}
}
if(!sprinkler_file.exists()){
try {
sprinkler_file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
MessageManager.consoleMessage("&c[CustomCrops] 洒水器数据文件生成失败!",Bukkit.getConsoleSender());
}
}
//载入data数据
FileConfiguration crop_data;
FileConfiguration sprinkler_data;
crop_data = YamlConfiguration.loadConfiguration(crop_file);
sprinkler_data = YamlConfiguration.loadConfiguration(sprinkler_file);
CustomCrops.cropManager = new CropManager(crop_data);
CustomCrops.sprinklerManager = new SprinklerManager(sprinkler_data);
//检测papi依赖
if(Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null){
new Placeholders(this).register();
MessageManager.consoleMessage("&#ccfbff-#ef96c5&[CustomCrops] 检测到PlaceHolderAPI 已启用季节变量!",Bukkit.getConsoleSender());
}
//启动成功
MessageManager.consoleMessage("&#ccfbff-#ef96c5&[CustomCrops] 自定义农作物插件已启用!作者:小默米 QQ:3266959688",Bukkit.getConsoleSender());
//this.getLogger().info("自定义农作物插件已启用!作者:小默米 QQ:3266959688");
}
@Override
public void onDisable() {
//关闭定时任务
if (CustomCrops.timer != null) {
CropTimer.stopTimer(CustomCrops.timer.getTaskID());
}
//保存缓存中的数据
CropManager.saveData();
SprinklerManager.saveData();
//备份
BackUp.backUpData();
MessageManager.consoleMessage(("&#ccfbff-#ef96c5&[CustomCrops] 自定义农作物插件已卸载!作者:小默米 QQ:3266959688"),Bukkit.getConsoleSender());
}
}

View File

@@ -0,0 +1,32 @@
package net.momirealms.customcrops;
import dev.lone.itemsadder.api.CustomFurniture;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
public class IAFurniture {
static FileConfiguration config = CustomCrops.instance.getConfig();
//放置IA自定义家具
public static void placeFurniture(String name, Location location){
CustomFurniture.spawn(name,location.getWorld().getBlockAt(location));
}
//根据位置获取盔甲架如果是洒水器返回true否则返回false
public static boolean getFromLocation(Location location, World world){
for(Entity entity : world.getNearbyEntities(location,0,0,0)){
if(entity instanceof ArmorStand){
if(CustomFurniture.byAlreadySpawned((ArmorStand) entity) != null){
if(CustomFurniture.byAlreadySpawned((ArmorStand) entity).getNamespacedID().equalsIgnoreCase(config.getString("config.sprinkler-1")) || CustomFurniture.byAlreadySpawned((ArmorStand) entity).getNamespacedID().equalsIgnoreCase(config.getString("config.sprinkler-2"))){
return true;
}
}
}
}
return false;
}
}

View File

@@ -0,0 +1,14 @@
package net.momirealms.customcrops;
import net.momirealms.customcrops.Libs.minedown.MineDown;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class MessageManager {
public static void consoleMessage(String s, CommandSender sender) {
sender.spigot().sendMessage(MineDown.parse(s));
}
public static void playerMessage(String s, Player player){
player.spigot().sendMessage(MineDown.parse(s));
}
}

View File

@@ -0,0 +1,39 @@
package net.momirealms.customcrops;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
public class Placeholders extends PlaceholderExpansion{
public Placeholders(CustomCrops customCrops) {
}
@Override
public @NotNull String getIdentifier() {
return "customcrops";
}
@Override
public @NotNull String getAuthor() {
return "XiaoMoMi";
}
@Override
public @NotNull String getVersion() {
return "1.0";
}
@Override
public String onRequest(OfflinePlayer player, String params) {
if(params.equalsIgnoreCase("season")){
return ConfigManager.Config.current
.replace("spring", ConfigManager.Config.spring)
.replace("summer", ConfigManager.Config.summer)
.replace("autumn", ConfigManager.Config.autumn)
.replace("winter", ConfigManager.Config.winter);
}
return null;
}
}

View File

@@ -0,0 +1,25 @@
package net.momirealms.customcrops.crops;
import net.momirealms.customcrops.CustomCrops;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
public class CropTimer {
int taskID;
public static void stopTimer(int ID) {
Bukkit.getScheduler().cancelTask(ID);
Bukkit.getServer().getScheduler().cancelTask(ID);
}
public CropTimer() {
TimeCheck tc = new TimeCheck();
BukkitTask task = tc.runTaskTimer(CustomCrops.instance, 1,1);
this.taskID = task.getTaskId();
}
public int getTaskID() {
return this.taskID;
}
}

View File

@@ -0,0 +1,33 @@
package net.momirealms.customcrops.crops;
import net.momirealms.customcrops.ConfigManager;
import net.momirealms.customcrops.CustomCrops;
import net.momirealms.customcrops.datamanager.CropManager;
import net.momirealms.customcrops.datamanager.SprinklerManager;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitScheduler;
public class TimeCheck extends BukkitRunnable {
BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
@Override
public void run() {
ConfigManager.Config.worlds.forEach(world ->{
long time = Bukkit.getWorld(world).getTime();
ConfigManager.Config.cropGrowTimeList.forEach(cropGrowTime -> {
if(time == cropGrowTime){
bukkitScheduler.runTaskAsynchronously(CustomCrops.instance, CropManager::CropGrow);
}
});
ConfigManager.Config.sprinklerWorkTimeList.forEach(sprinklerTime -> {
if(time == sprinklerTime){
bukkitScheduler.runTaskAsynchronously(CustomCrops.instance, SprinklerManager::SprinklerWork);
}
});
});
}
}

View File

@@ -0,0 +1,53 @@
package net.momirealms.customcrops.datamanager;
import net.momirealms.customcrops.CustomCrops;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class BackUp {
public static void backUpData(){
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
File crop_data = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml");
File cropBackUp = new File(CustomCrops.instance.getDataFolder(), "backups/"+ format.format(date) + "/" + "crop-data.yml");
File sprinkler_data = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml");
File sprinklerBackUp = new File(CustomCrops.instance.getDataFolder(), "backups/"+ format.format(date) + "/" + "sprinkler-data.yml");
try {
BackUp.backUp(crop_data,cropBackUp);
} catch (IOException e) {
e.printStackTrace();
}
try {
BackUp.backUp(sprinkler_data,sprinklerBackUp);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void backUp(File file_from, File file_to) throws IOException {
if(!file_to.exists()){
file_to.getParentFile().mkdirs();
}
FileInputStream fis = new FileInputStream(file_from);
if(!file_to.exists()){
file_to.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file_to);
byte[] b = new byte[1024];
int len;
while ((len = fis.read(b))!= -1){
fos.write(b,0,len);
}
fos.close();
fis.close();
}
}

View File

@@ -0,0 +1,322 @@
package net.momirealms.customcrops.datamanager;
import dev.lone.itemsadder.api.CustomBlock;
import net.momirealms.customcrops.ConfigManager;
import net.momirealms.customcrops.CustomCrops;
import net.momirealms.customcrops.MessageManager;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.scheduler.BukkitScheduler;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class CropManager {
public static ConcurrentHashMap<Location, String> instances;
//4W性能测试
public static void testData_1(){
for(int i = -100; i < 100; i++){
for(int j = -100; j < 100; j++){
Location tempLoc = new Location(Bukkit.getWorld("world"),i,100,j);
String name = "spring";
instances.put(tempLoc, name);
}
}
}
//20W性能测试
public static void testData_2(){
for(int i = -100000; i < 100000; i++){
Location tempLoc = new Location(Bukkit.getWorld("world"),i,100,i);
String name = "spring";
instances.put(tempLoc, name);
}
}
//开服的时候将文件的数据读入
public CropManager(FileConfiguration data) {
FileConfiguration config = CustomCrops.instance.getConfig();
File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml");
data = YamlConfiguration.loadConfiguration(file);
try {
for (String world : config.getStringList("config.whitelist-worlds")) {
CropManager.instances = new ConcurrentHashMap<Location, String>();
if(data.getConfigurationSection(world) != null){
for (String coordinate : data.getConfigurationSection(world).getKeys(false)) {
Location tempLocation = new Location(Bukkit.getWorld(world), (double)Integer.parseInt(coordinate.split(",")[0]), (double)Integer.parseInt(coordinate.split(",")[1]), (double)Integer.parseInt(coordinate.split(",")[2]));
String season = data.getString(world + "." + coordinate);
CropManager.instances.put(tempLocation, season);
}
}
}
}
catch (Exception e) {
CropManager.instances = new ConcurrentHashMap<Location, String>();
e.printStackTrace();
}
saveData();
}
/*
//根据世界名获取所有的农作物
public static List<Location> getCrops(World world){
FileConfiguration config = CustomCrops.instance.getConfig();
File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml");
FileConfiguration data;
data = YamlConfiguration.loadConfiguration(file);
List<Location> locations = new ArrayList<Location>();
if (config.getStringList("config.whitelist-worlds").contains(world.getName())){
if(data.contains(world.getName())){
data.getConfigurationSection(world.getName()).getKeys(false).forEach(key ->{
String[] string_list = key.split(",");
if (config.getBoolean("config.only-grow-in-loaded-chunks")){
if (world.isChunkLoaded(Integer.parseInt(string_list[0])/16, Integer.parseInt(string_list[2])/16)){
locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2])));
}
}else {
locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2])));
}
});
}
}
return locations;
}
*/
//保存数据
public static void saveData(){
File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml");
FileConfiguration data;
data = YamlConfiguration.loadConfiguration(file);
if (CropManager.instances != null) {
//性能更高
Set<Map.Entry<Location, String>> en = instances.entrySet();
for(Map.Entry<Location, String> entry : en){
data.set(entry.getKey().getWorld().getName() + "." + entry.getKey().getBlockX() + "," + entry.getKey().getBlockY()+ ","+entry.getKey().getBlockZ(), entry.getValue());
}
}
else {
CropManager.instances = new ConcurrentHashMap<Location, String>();
Bukkit.getConsoleSender().sendMessage("错误:空数据");
}
try {
data.save(file);
}
catch (IOException e) {
e.printStackTrace();
CustomCrops.instance.getLogger().warning("农作物数据保存出错");
}
}
public static void putInstance(Location location, String season) {
CropManager.instances.put(location, season);
}
//清理无效的农作物并生长
public static void CropGrow() {
/*
阶段1更新数据
*/
long start1 = System.currentTimeMillis();
FileConfiguration config = CustomCrops.instance.getConfig();
File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml");
FileConfiguration data;
data = YamlConfiguration.loadConfiguration(file);
if (CropManager.instances != null) {
//性能更高
Set<Map.Entry<Location, String>> en = instances.entrySet();
for(Map.Entry<Location, String> entry : en){
Location key = entry.getKey();
data.set(key.getWorld().getName() + "." + key.getBlockX() + "," + key.getBlockY()+ ","+ key.getBlockZ(), entry.getValue());
}
}
long finish1 = System.currentTimeMillis();
MessageManager.consoleMessage("&#ccfbff-#ef96c5&[CustomCrops|性能监测] &f农作物数据更新耗时&a" + String.valueOf(finish1-start1) + "&fms",Bukkit.getConsoleSender());
/*
阶段2清理数据内无效的农作物
*/
long start2 = System.currentTimeMillis();
List<Location> locations = new ArrayList<Location>();
ConfigManager.Config.worlds.forEach(worldName ->{
if(data.contains(worldName)){
World world = Bukkit.getWorld(worldName);
data.getConfigurationSection(worldName).getKeys(false).forEach(key ->{
String[] string_list = StringUtils.split(key,",");
if (world.isChunkLoaded(Integer.parseInt(string_list[0])/16, Integer.parseInt(string_list[2])/16)){
Location tempLoc = new Location(world,Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2]));
if(tempLoc.getBlock().getType() != Material.TRIPWIRE){
CropManager.instances.remove(tempLoc);
data.set(worldName+"."+string_list[0]+","+string_list[1]+","+string_list[2], null);
}else {
locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2])));
}
}
});
}
});
long finish2 = System.currentTimeMillis();
MessageManager.consoleMessage("&#ccfbff-#ef96c5&[CustomCrops|性能监测] &f农作物缓存清理耗时&a" + String.valueOf(finish2-start2) + "&fms",Bukkit.getConsoleSender());
/*
阶段3保存文件
*/
try{
data.save(file);
}catch (IOException e){
e.printStackTrace();
CustomCrops.instance.getLogger().warning("农作物缓存清理保存出错!");
}
/*
阶段4农作物生长判断
*/
long start3 = System.currentTimeMillis();
BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
ConfigManager.Config.worlds.forEach(worldName -> {
World world = Bukkit.getWorld(worldName);
locations.forEach(seedLocation -> {
Location potLocation = seedLocation.clone().subtract(0,1,0);
Block seedBlock = seedLocation.getBlock();
Block potBlock = potLocation.getBlock();
String[] seasons = StringUtils.split(data.getString(worldName + "." + seedLocation.getBlockX() + "," + seedLocation.getBlockY() + "," + seedLocation.getBlockZ()),",");
if (CustomBlock.byAlreadyPlaced(potBlock) != null && CustomBlock.byAlreadyPlaced(seedBlock) != null){
String seedNamespace = CustomBlock.byAlreadyPlaced(seedBlock).getNamespacedID();
if (CustomBlock.byAlreadyPlaced(potBlock).getNamespacedID().equalsIgnoreCase(ConfigManager.Config.watered_pot) && seedNamespace.contains("stage")){
if (seedNamespace.equalsIgnoreCase(ConfigManager.Config.dead)){
return;
}
String[] split = StringUtils.split(seedNamespace,":");
String[] cropNameList = StringUtils.split(split[1],"_");
Label_out:
if(ConfigManager.Config.season){
if(ConfigManager.Config.greenhouse){
for(int i = 1; i <= ConfigManager.Config.range; i++){
Location tempLocation = seedLocation.clone().add(0,i,0);
if (CustomBlock.byAlreadyPlaced(tempLocation.getBlock()) != null){
if(CustomBlock.byAlreadyPlaced(tempLocation.getBlock()).getNamespacedID().equalsIgnoreCase(ConfigManager.Config.glass)){
break Label_out;
}
}
}
}
boolean wrongSeason = true;
for(String season : seasons){
if (Objects.equals(season, ConfigManager.Config.current)) {
wrongSeason = false;
break;
}
}
if(wrongSeason){
bukkitScheduler.callSyncMethod(CustomCrops.instance, () -> {
CustomBlock.remove(seedLocation);
CustomBlock.place(ConfigManager.Config.dead, seedLocation);
return null;
});
return;
}
}
int nextStage = Integer.parseInt(cropNameList[2]) + 1;
if (CustomBlock.getInstance( split[0] +":"+cropNameList[0] + "_stage_" + nextStage) != null) {
bukkitScheduler.callSyncMethod(CustomCrops.instance, () ->{
CustomBlock.remove(potLocation);
CustomBlock.place(ConfigManager.Config.pot, potLocation);
if(Math.random()< ConfigManager.Config.grow_chance){
CustomBlock.remove(seedLocation);
CustomBlock.place(split[0] + ":" + cropNameList[0] + "_stage_" + nextStage,seedLocation);
}
return null;
});
}else if(ConfigManager.Config.big){
//农作物巨大化
if(config.getConfigurationSection("crops." + cropNameList[0]).getKeys(false).contains("gigantic")){
bukkitScheduler.callSyncMethod(CustomCrops.instance, () ->{
CustomBlock.remove(potLocation);
CustomBlock.place(ConfigManager.Config.pot, potLocation);
if(ConfigManager.Config.big_chance > Math.random()){
CustomBlock.remove(seedLocation);
CustomBlock.place(config.getString("crops." + cropNameList[0] + ".gigantic"),seedLocation);
}
return null;
});
}
}
}else if(CustomBlock.byAlreadyPlaced(potBlock).getNamespacedID().equalsIgnoreCase(ConfigManager.Config.pot) && seedNamespace.contains("stage")){
if (seedNamespace.equalsIgnoreCase(ConfigManager.Config.dead)){
return;
}
if(ConfigManager.Config.season) {
if(ConfigManager.Config.greenhouse){
for(int i = 1; i <= ConfigManager.Config.range; i++){
Location tempLocation = seedLocation.clone().add(0,i,0);
if (CustomBlock.byAlreadyPlaced(tempLocation.getBlock()) != null){
if(CustomBlock.byAlreadyPlaced(tempLocation.getBlock()).getNamespacedID().equalsIgnoreCase(ConfigManager.Config.glass)){
return;
}
}
}
}
boolean wrongSeason = true;
for (String season : seasons) {
if (Objects.equals(season, ConfigManager.Config.current)) {
wrongSeason = false;
break;
}
}
if (wrongSeason) {
bukkitScheduler.callSyncMethod(CustomCrops.instance, () -> {
CustomBlock.remove(seedLocation);
CustomBlock.place(ConfigManager.Config.dead, seedLocation);
return null;
});
}
}
}
}
});
});
long finish3 = System.currentTimeMillis();
MessageManager.consoleMessage("&#ccfbff-#ef96c5&[CustomCrops|性能监测] &f农作物生长判断耗时&a" + String.valueOf(finish3-start3) + "&fms",Bukkit.getConsoleSender());
}
/*
//清理无效的农作物
public static void cleanLoadedCache() {
File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml");
FileConfiguration data;
data = YamlConfiguration.loadConfiguration(file);
ConfigManager.Config.worlds.forEach(worldName ->{
if(data.contains(worldName)){
World world = Bukkit.getWorld(worldName);
data.getConfigurationSection(worldName).getKeys(false).forEach(key ->{
String[] string_list = StringUtils.split(key,",");
if (world.isChunkLoaded(Integer.parseInt(string_list[0])/16, Integer.parseInt(string_list[2])/16)){
Location tempLoc = new Location(world,Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2]));
if(world.getBlockAt(tempLoc).getType() != Material.TRIPWIRE){
CropManager.instances.remove(tempLoc);
data.set(worldName+"."+string_list[0]+","+string_list[1]+","+string_list[2], null);
}
}
});
}
});
try{
data.save(file);
}catch (IOException e){
e.printStackTrace();
CustomCrops.instance.getLogger().warning("农作物缓存清理保存出错!");
}
}
*/
}

View File

@@ -0,0 +1,42 @@
package net.momirealms.customcrops.datamanager;
import dev.lone.itemsadder.api.CustomBlock;
import net.momirealms.customcrops.ConfigManager;
import org.bukkit.Location;
import org.bukkit.block.Block;
public class MaxCropsPerChunk {
public static boolean maxCropsPerChunk(Location location){
if(!ConfigManager.Config.limit){
return false;
}
int maxY = ConfigManager.Config.maxh;
int minY = ConfigManager.Config.minh;
int maxAmount = ConfigManager.Config.max_crop;
int n = 1;
Location chunkLocation = new Location(location.getWorld(),location.getChunk().getX()*16,minY,location.getChunk().getZ()*16);
Label_out:
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
Location square = chunkLocation.clone().add(i, 0.0, j);
for (int k = minY; k <= maxY; ++k) {
square.add(0.0, 1.0, 0.0);
Block b = square.getBlock();
if(CustomBlock.byAlreadyPlaced(b)!= null){
if (CustomBlock.byAlreadyPlaced(b).getNamespacedID().contains("stage")) {
if (n++ > maxAmount) {
break Label_out;
}
}
}
}
}
}
return n > maxAmount;
}
}

View File

@@ -0,0 +1,43 @@
package net.momirealms.customcrops.datamanager;
import net.momirealms.customcrops.ConfigManager;
import net.momirealms.customcrops.IAFurniture;
import org.bukkit.Location;
import org.bukkit.World;
public class MaxSprinklersPerChunk {
public static boolean maxSprinklersPerChunk(Location location){
if(!ConfigManager.Config.limit){
return false;
}
int maxY = ConfigManager.Config.maxh;
int minY = ConfigManager.Config.minh;
int maxAmount = ConfigManager.Config.max_sprinkler;
int n = 1;
Location chunkLocation = new Location(location.getWorld(),location.getChunk().getX()*16,minY,location.getChunk().getZ()*16);
World world = location.getWorld();
Label_out:
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
Location square = chunkLocation.clone().add(i+0.5, 0.5, j+0.5);
for (int k = minY; k <= maxY; ++k) {
square.add(0.0, 1.0, 0.0);
if(IAFurniture.getFromLocation(square, world)){
if (n++ > maxAmount) {
break Label_out;
}
}
}
}
}
return n > maxAmount;
}
public static boolean alreadyPlaced(Location location){
return IAFurniture.getFromLocation(location.clone().add(0.5, 1.5, 0.5), location.getWorld());
}
}

View File

@@ -0,0 +1,29 @@
package net.momirealms.customcrops.datamanager;
import net.momirealms.customcrops.ConfigManager;
import net.momirealms.customcrops.CustomCrops;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.Objects;
public class NextSeason {
public static void changeSeason(){
FileConfiguration config = CustomCrops.instance.getConfig();
String currentSeason = ConfigManager.Config.current;
String nextSeason = switch (Objects.requireNonNull(currentSeason)) {
case "spring" -> "summer";
case "summer" -> "autumn";
case "autumn" -> "winter";
case "winter" -> "spring";
default -> null;
};
if(nextSeason != null){
config.set("current-season", nextSeason);
ConfigManager.Config.current = nextSeason;
CustomCrops.instance.saveConfig();
}else {
CustomCrops.instance.getLogger().warning("季节配置文件出错!");
}
}
}

View File

@@ -0,0 +1,223 @@
package net.momirealms.customcrops.datamanager;
import dev.lone.itemsadder.api.CustomBlock;
import net.momirealms.customcrops.ConfigManager;
import net.momirealms.customcrops.CustomCrops;
import net.momirealms.customcrops.IAFurniture;
import net.momirealms.customcrops.MessageManager;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class SprinklerManager {
public static ConcurrentHashMap<Location, String> instances;
//10W性能测试
public static void testData_3(){
for(int i = -50000; i < 50000; i++){
Location tempLoc = new Location(Bukkit.getWorld("world"),i,100,i);
String name = "s1";
instances.put(tempLoc, name);
}
}
//开服的时候将文件的数据读入
public SprinklerManager(FileConfiguration data) {
FileConfiguration config = CustomCrops.instance.getConfig();
File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml");
data = YamlConfiguration.loadConfiguration(file);
try {
for (String world : config.getStringList("config.whitelist-worlds")) {
SprinklerManager.instances = new ConcurrentHashMap<Location, String>();
if(data.getConfigurationSection(world) != null){
for (String coordinate : data.getConfigurationSection(world).getKeys(false)) {
Location tempLocation = new Location(Bukkit.getWorld(world), Integer.parseInt(coordinate.split(",")[0]), Integer.parseInt(coordinate.split(",")[1]), Integer.parseInt(coordinate.split(",")[2]));
String type = data.getString(world + "." + coordinate);
SprinklerManager.instances.put(tempLocation, type);
}
}
}
}
catch (Exception e) {
SprinklerManager.instances = new ConcurrentHashMap<Location, String>();
e.printStackTrace();
}
saveData();
}
/*
//根据世界名获取所有的洒水器
public static List<Location> getSprinklers(World world){
FileConfiguration config = CustomCrops.instance.getConfig();
File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml");
FileConfiguration data;
data = YamlConfiguration.loadConfiguration(file);
List<Location> locations = new ArrayList<Location>();
if (config.getStringList("config.whitelist-worlds").contains(world.getName())){
if(data.contains(world.getName())){
data.getConfigurationSection(world.getName()).getKeys(false).forEach(key ->{
String[] string_list = key.split(",");
//只返回被加载的区块中的洒水器坐标
if (config.getBoolean("config.only-grow-in-loaded-chunks")){
if (world.isChunkLoaded(Integer.parseInt(string_list[0])/16, Integer.parseInt(string_list[2])/16)){
locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2])));
}
}else {
locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2])));
}
});
}
}
return locations;
}
*/
//保存数据
public static void saveData(){
File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml");
FileConfiguration data;
data = YamlConfiguration.loadConfiguration(file);
if (SprinklerManager.instances != null) {
//性能更高
Set<Map.Entry<Location, String>> en = instances.entrySet();
for(Map.Entry<Location, String> entry : en){
data.set(entry.getKey().getWorld().getName() + "." + entry.getKey().getBlockX() + "," + entry.getKey().getBlockY()+ ","+entry.getKey().getBlockZ(), entry.getValue());
}
}
else {
SprinklerManager.instances = new ConcurrentHashMap<Location, String>();
Bukkit.getConsoleSender().sendMessage("错误:空数据");
}
try {
data.save(file);
}
catch (IOException e) {
e.printStackTrace();
CustomCrops.instance.getLogger().warning("洒水器数据保存出错");
}
}
public static void putInstance(Location location, String type) {
SprinklerManager.instances.put(location, type);
}
public static void SprinklerWork() {
/*
阶段1更新数据
*/
long start1 = System.currentTimeMillis();
File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml");
FileConfiguration data;
data = YamlConfiguration.loadConfiguration(file);
if (SprinklerManager.instances != null) {
//性能更高
Set<Map.Entry<Location, String>> en = instances.entrySet();
for(Map.Entry<Location, String> entry : en){
data.set(entry.getKey().getWorld().getName() + "." + entry.getKey().getBlockX() + "," + entry.getKey().getBlockY()+ ","+entry.getKey().getBlockZ(), entry.getValue());
}
}
long finish1 = System.currentTimeMillis();
MessageManager.consoleMessage("&#ccfbff-#ef96c5&[CustomCrops|性能监测] &f洒水器数据更新耗时&a" + String.valueOf(finish1-start1) + "&fms",Bukkit.getConsoleSender());
/*
阶段2清理数据内无效的农作物
*/
Bukkit.getScheduler().callSyncMethod(CustomCrops.instance,()->{
long start2 = System.currentTimeMillis();
//map不能一边循环一边删除
//创建一个新的HashSet,用作循环
//检测碰撞体积需要同步
List<Location> locations = new ArrayList<Location>();
ConfigManager.Config.worlds.forEach(worldName ->{
if(data.contains(worldName)){
World world = Bukkit.getWorld(worldName);
data.getConfigurationSection(worldName).getKeys(false).forEach(key ->{
String[] string_list = StringUtils.split(key,",");
if (world.isChunkLoaded(Integer.parseInt(string_list[0])/16, Integer.parseInt(string_list[2])/16)){
Location tempLoc = new Location(world,Double.parseDouble(string_list[0])+0.5,Double.parseDouble(string_list[1])+0.5,Double.parseDouble(string_list[2])+0.5);
if(!IAFurniture.getFromLocation(tempLoc, world)){
SprinklerManager.instances.remove(tempLoc);
data.set(worldName+"."+string_list[0]+","+string_list[1]+","+string_list[2], null);
}else {
locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2])));
}
}
});
}
});
/*
Set<Location> key = new HashSet(instances.keySet());
for(Location u_key : key) {
if (!IAFurniture.getFromLocation(u_key.clone().add(0.5,0.5,0.5), u_key.getWorld())){
SprinklerManager.instances.remove(u_key);
data.set(u_key.getWorld().getName()+"."+u_key.getBlockX()+","+u_key.getBlockY()+","+u_key.getBlockZ(), null);
}else {
locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2])));
}
}
*/
long finish2 = System.currentTimeMillis();
MessageManager.consoleMessage("&#ccfbff-#ef96c5&[CustomCrops|性能监测] &f洒水器缓存清理耗时&a" + String.valueOf(finish2-start2) + "&fms",Bukkit.getConsoleSender());
Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.instance,()->{
/*
阶段3保存数据
*/
try{
data.save(file);
}catch (IOException e){
e.printStackTrace();
CustomCrops.instance.getLogger().warning("洒水器缓存清理保存出错!");
}
/*
阶段4洒水器工作
*/
long start3 = System.currentTimeMillis();
ConfigManager.Config.worlds.forEach(worldName -> {
World world = Bukkit.getWorld(worldName);
locations.forEach(location -> {
String type = data.getString(worldName + "." + location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ());
if(type.equals("s1")){
for(int i = -1; i <= 1;i++){
for (int j = -1; j <= 1; j++){
Location tempLoc = location.clone().add(i,-1,j);
waterPot(tempLoc, world);
}
}
}else{
for(int i = -2; i <= 2;i++){
for (int j = -2; j <= 2; j++){
Location tempLoc = location.clone().add(i,-1,j);
waterPot(tempLoc, world);
}
}
}
});
});
long finish3 = System.currentTimeMillis();
MessageManager.consoleMessage("&#ccfbff-#ef96c5&[CustomCrops|性能监测] &f洒水器工作耗时&a" + String.valueOf(finish3-start3) + "&fms",Bukkit.getConsoleSender());
});
return null;
});
}
private static void waterPot(Location tempLoc, World world) {
if(CustomBlock.byAlreadyPlaced(tempLoc.getBlock()) != null){
if(CustomBlock.byAlreadyPlaced(tempLoc.getBlock()).getNamespacedID().equalsIgnoreCase(ConfigManager.Config.pot)){
Bukkit.getScheduler().callSyncMethod(CustomCrops.instance,()->{
CustomBlock.remove(tempLoc);
CustomBlock.place((ConfigManager.Config.watered_pot), tempLoc);
return null;
});
}
}
}
}

View File

@@ -0,0 +1,43 @@
package net.momirealms.customcrops.integrations;
import net.momirealms.customcrops.ConfigManager;
import org.bukkit.Location;
import org.bukkit.entity.Player;
public class IntegrationCheck {
//收获权限检测
public static boolean HarvestCheck(Location location, Player player){
if(ConfigManager.Config.king){
if(KingdomsXIntegrations.checkKDBuild(location,player)){
return true;
}
}
if(ConfigManager.Config.res){
if(ResidenceIntegrations.checkResHarvest(location,player)){
return true;
}
}
if(ConfigManager.Config.wg){
return WorldGuardIntegrations.checkWGHarvest(location, player);
}
return false;
}
//种植等权限检测
public static boolean PlaceCheck(Location location, Player player){
if(ConfigManager.Config.king){
if(KingdomsXIntegrations.checkKDBuild(location,player)){
return true;
}
}
if(ConfigManager.Config.res){
if(ResidenceIntegrations.checkResBuild(location,player)){
return true;
}
}
if(ConfigManager.Config.wg){
return WorldGuardIntegrations.checkWGBuild(location, player);
}
return false;
}
}

View File

@@ -0,0 +1,25 @@
package net.momirealms.customcrops.integrations;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.kingdoms.constants.group.Kingdom;
import org.kingdoms.constants.land.Land;
import org.kingdoms.constants.player.KingdomPlayer;
public class KingdomsXIntegrations {
public static boolean checkKDBuild(Location location, Player player){
KingdomPlayer kp = KingdomPlayer.getKingdomPlayer(player);
Land land = Land.getLand(location);
if (land == null) return false;
if (land.isClaimed()) {
Kingdom cropKingdom = land.getKingdom();
if (kp.getKingdom() != null) {
Kingdom kingdom = kp.getKingdom();
return kingdom != cropKingdom;
}else {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,31 @@
package net.momirealms.customcrops.integrations;
import com.bekvon.bukkit.residence.containers.Flags;
import com.bekvon.bukkit.residence.protection.ClaimedResidence;
import com.bekvon.bukkit.residence.protection.FlagPermissions;
import com.bekvon.bukkit.residence.protection.ResidencePermissions;
import org.bukkit.Location;
import org.bukkit.entity.Player;
public class ResidenceIntegrations {
public static boolean checkResBuild(Location location, Player player){
FlagPermissions.addFlag("build");
ClaimedResidence res = com.bekvon.bukkit.residence.Residence.getInstance().getResidenceManager().getByLoc(location);
if(res!=null){
ResidencePermissions perms = res.getPermissions();
boolean hasPermission = perms.playerHas(player, Flags.build, true);
return !hasPermission;
}
return false;
}
public static boolean checkResHarvest(Location location, Player player){
FlagPermissions.addFlag("harvest");
ClaimedResidence res = com.bekvon.bukkit.residence.Residence.getInstance().getResidenceManager().getByLoc(location);
if(res!=null){
ResidencePermissions perms = res.getPermissions();
boolean hasPermission = perms.playerHas(player, Flags.harvest, true);
return !hasPermission;
}
return false;
}
}

View File

@@ -0,0 +1,30 @@
package net.momirealms.customcrops.integrations;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import com.sk89q.worldguard.protection.regions.RegionQuery;
import org.bukkit.Location;
import org.bukkit.entity.Player;
public class WorldGuardIntegrations {
public static boolean checkWGBuild(Location loc,Player player){
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
RegionQuery query = container.createQuery();
return !query.testState(BukkitAdapter.adapt(loc), localPlayer, Flags.BUILD);
}
public static boolean checkWGHarvest(Location loc,Player player){
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
RegionQuery query = container.createQuery();
return !query.testState(BukkitAdapter.adapt(loc), localPlayer, Flags.BLOCK_BREAK);
}
}

View File

@@ -0,0 +1,499 @@
package net.momirealms.customcrops.Libs.minedown;
/*
* Copyright (c) 2017 Max Lee (https://github.com/Phoenix616)
*
* 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.
*/
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.HoverEvent;
import java.util.Map;
/**
* <h2>MineDown</h2>
* A MarkDown inspired markup for Minecraft chat components
* <p>
* This lets you convert string messages into chat components by using a custom mark up syntax
* which is loosely based on MarkDown while still supporting legacy formatting codes.
*
* <table>
* <caption><strong> Inline Formatting </strong></caption>
* <tr><td> Color legacy </td><td><code> &amp;6Text </code></td><td> {@link ChatColor} codes </td></tr>
* <tr><td> Color </td><td><code> &amp;gold&amp;Text </code></td><td> {@link ChatColor} codes </td></tr>
* <tr><td> RGB Hex Color </td><td><code> &amp;ff00ff&amp;Text </code></td><td> Full hexadecimal format </td></tr>
* <tr><td> RGB Hex Color </td><td><code> &amp;f0f&amp;Text </code></td><td> Short format (equivalent to long one) </td></tr>
* <tr><td> Bold </td><td><code> **Text** </code></td></tr>
* <tr><td> Italic </td><td><code> ##Text## </code></td></tr>
* <tr><td> Underlined </td><td><code> __Text__ </code></td></tr>
* <tr><td> Strikethrough </td><td><code> ~~Text~~ </code></td></tr>
* <tr><td> Obfuscated </td><td><code> ??Text?? </code></td></tr>
* </table>
*
* <h3>Events</h3>
* You can define click and hover events with the commonly used MarkDown link syntax.
* <p>
* <table>
* <caption><strong> Simple Syntax </strong></caption>
* <tr><td> General syntax </td><td><code> [Text](text-color text-formatting... link hover text) </code></td></tr>
* <tr><td> Simple Link </td><td><code> [Text](https://example.com) </code></td></tr>
* <tr><td> Simple Command </td><td><code> [Text](/command to run) </code></td></tr>
* <tr><td> Link + Hover </td><td><code> [Text](https://example.com Hover Text) </code></td></tr>
* <tr><td> Text formatting + Link + Hover </td><td><code> [Text](blue underline https://example.com Hover Text) </code></td></tr>
* </table>
* <p>
* <table>
* <caption><strong> Advanced Syntax </strong></caption>
* <tr><td> General syntax </td><td><code> [Text](action=value) </code></td><td> {@link ClickEvent.Action}, {@link HoverEvent.Action} </td></tr>
* <tr><td> Link </td><td><code> [Text](open_url=https://example.com) </code></td></tr>
* <tr><td> Color </td><td><code> [Text](color=red) </code></td></tr>
* <tr><td> RGB Hex Color </td><td><code> [Text](color=#ff00ff) </code></td><td> Full hexadecimal format </td></tr>
* <tr><td> RGB Hex Color </td><td><code> [Text](color=#f0f) </code></td><td> Short format (equivalent to long one) </td></tr>
* <tr><td> Formatting </td><td><code> [Text](format=underline,bold) </code></td></tr>
* <tr><td> Font </td><td><code> [Text](format=underline,bold) </code></td></tr>
* <tr><td> Run Command </td><td><code> [Text](run_command=/command string) </code></td></tr>
* <tr><td> Suggest Command </td><td><code> [Text](suggest_command=/command) </code></td></tr>
* <tr><td> Simple Hover </td><td><code> [Text](hover=Hover Text) </code></td></tr>
* <tr><td> Hover Text </td><td><code> [Text](show_text=Hover Text) </code></td></tr>
* <tr><td> Hover Entity Info </td><td><code> [Text](show_entity=uuid:pig Name) </code></td></tr>
* <tr><td> Hover Item Info </td><td><code> [Text](show_item=stone*2 nbt...) </code></td></tr>
* </table>
* <p>
* All advanced settings can be chained/included in a event definition.
* You can't however add multiple different colors or click and hover actions!
*/
public class MineDown {
public static final String FONT_PREFIX = "font=";
public static final String COLOR_PREFIX = "color=";
public static final String FORMAT_PREFIX = "format=";
public static final String HOVER_PREFIX = "hover=";
public static final String INSERTION_PREFIX = "insert=";
private String message;
private final Replacer replacer = new Replacer();
private final MineDownParser parser = new MineDownParser();
private BaseComponent[] baseComponents = null;
private boolean replaceFirst = Boolean.getBoolean("de.themoep.minedown.replacefirst");
/**
* Create a new MineDown builder with a certain message
* @param message The message to parse
*/
public MineDown(String message) {
this.message = message;
}
/**
* Parse a MineDown string to components
* @param message The message to translate
* @param replacements Optional placeholder replacements
* @return The parsed components
*/
public static BaseComponent[] parse(String message, String... replacements) {
return new MineDown(message).replace(replacements).toComponent();
}
/**
* Convert components to a MineDown string
* @param components The components to convert
* @return The components represented as a MineDown string
*/
public static String stringify(BaseComponent[] components) {
return new MineDownStringifier().stringify(components);
}
/**
* Parse and convert the message to the component
* @return The parsed component message
*/
public BaseComponent[] toComponent() {
if (baseComponents() == null) {
if (replaceFirst()) {
Replacer componentReplacer = new Replacer();
for (Map.Entry<String, BaseComponent[]> entry : replacer().componentReplacements().entrySet()) {
componentReplacer.replace(entry.getKey(), stringify(entry.getValue()));
}
baseComponents = parser().parse(componentReplacer.replaceIn(replacer().replaceIn(message()))).create();
} else {
baseComponents = replacer().replaceIn(parser().parse(message()).create());
}
}
return baseComponents();
}
/**
* Remove a cached component and re-parse the next time {@link #toComponent} is called
*/
private void reset() {
baseComponents = null;
}
/**
* Set whether or not replacements should be replaced before or after the components are created.
* When replacing first it will not replace any placeholders with component replacement values!
* Default is after. (replaceFirst = false)
* @param replaceFirst Whether or not to replace first or parse first
* @return The MineDown instance
*/
public MineDown replaceFirst(boolean replaceFirst) {
reset();
this.replaceFirst = replaceFirst;
return this;
}
/**
* Get whether or not replacements should be replaced before or after the components are created.
* When replacing first it will not replace any placeholders with component replacement values!
* Default is after. (replaceFirst = false)
* @return Whether or not to replace first or parse first
*/
public boolean replaceFirst() {
return replaceFirst;
}
/**
* Add an array with placeholders and values that should get replaced in the message
* @param replacements The replacements, nth element is the placeholder, n+1th the value
* @return The MineDown instance
*/
public MineDown replace(String... replacements) {
reset();
replacer().replace(replacements);
return this;
}
/**
* Add a map with placeholders and values that should get replaced in the message
* @param replacements The replacements mapped placeholder to value
* @return The MineDown instance
*/
public MineDown replace(Map<String, ?> replacements) {
reset();
replacer().replace(replacements);
return this;
}
/**
* Add a placeholder to component mapping that should get replaced in the message
* @param placeholder The placeholder to replace
* @param replacement The replacement components
* @return The Replacer instance
*/
public MineDown replace(String placeholder, BaseComponent... replacement) {
reset();
replacer().replace(placeholder,replacement);
return this;
}
/**
* Set the placeholder indicator for both prefix and suffix
* @param placeholderIndicator The character to use as a placeholder indicator
* @return The MineDown instance
*/
public MineDown placeholderIndicator(String placeholderIndicator) {
placeholderPrefix(placeholderIndicator);
placeholderSuffix(placeholderIndicator);
return this;
}
/**
* Set the placeholder indicator's prefix character
* @param placeholderPrefix The character to use as the placeholder indicator's prefix
* @return The MineDown instance
*/
public MineDown placeholderPrefix(String placeholderPrefix) {
reset();
replacer().placeholderPrefix(placeholderPrefix);
return this;
}
/**
* Get the placeholder indicator's prefix character
* @return The placeholder indicator's prefix character
*/
public String placeholderPrefix() {
return replacer().placeholderPrefix();
}
/**
* Set the placeholder indicator's suffix character
* @param placeholderSuffix The character to use as the placeholder indicator's suffix
* @return The MineDown instance
*/
public MineDown placeholderSuffix(String placeholderSuffix) {
reset();
replacer().placeholderSuffix(placeholderSuffix);
return this;
}
/**
* Get the placeholder indicator's suffix character
* @return The placeholder indicator's suffix character
*/
public String placeholderSuffix() {
return replacer().placeholderSuffix();
}
/**
* Set whether or not the case of the placeholder should be ignored when replacing
* @param ignorePlaceholderCase Whether or not to ignore the case of the placeholders
* @return The MineDown instance
*/
public MineDown ignorePlaceholderCase(boolean ignorePlaceholderCase) {
reset();
replacer().ignorePlaceholderCase(ignorePlaceholderCase);
return this;
}
/**
* Get whether or not the case of the placeholder should be ignored when replacing
* @return Whether or not to ignore the case of the placeholders
*/
public boolean ignorePlaceholderCase() {
return replacer().ignorePlaceholderCase();
}
/**
* Enable or disable the translation of legacy color codes
* @param translateLegacyColors Whether or not to translate legacy color codes (Default: true)
* @return The MineDown instance
* @deprecated Use {@link #enable(MineDownParser.Option)} and {@link #disable(MineDownParser.Option)}
*/
@Deprecated
public MineDown translateLegacyColors(boolean translateLegacyColors) {
reset();
parser().translateLegacyColors(translateLegacyColors);
return this;
}
/**
* Detect urls in strings and add events to them? (Default: true)
* @param enabled Whether or not to detect URLs and add events to them
* @return The MineDown instance
*/
public MineDown urlDetection(boolean enabled) {
reset();
parser().urlDetection(enabled);
return this;
}
/**
* Automatically add http to values of open_url when there doesn't exist any? (Default: true)
* @param enabled Whether or not to automatically add http when missing
* @return The MineDown instance
*/
public MineDown autoAddUrlPrefix(boolean enabled) {
reset();
parser().autoAddUrlPrefix(enabled);
return this;
}
/**
* The text to display when hovering over an URL
* @param text The text to display when hovering over an URL
* @return The MineDown instance
*/
public MineDown urlHoverText(String text) {
reset();
parser().urlHoverText(text);
return this;
}
/**
* Set the max width the hover text should have.
* Minecraft itself will wrap after 60 characters.
* Won't apply if the text already includes new lines.
* @param hoverTextWidth The url hover text length
* @return The MineDown instance
*/
public MineDown hoverTextWidth(int hoverTextWidth) {
reset();
parser().hoverTextWidth(hoverTextWidth);
return this;
}
/**
* Enable an option. Unfilter it if you filtered it before.
* @param option The option to enable
* @return The MineDown instance
*/
public MineDown enable(MineDownParser.Option option) {
reset();
parser().enable(option);
return this;
}
/**
* Disable an option. Disabling an option will stop the parser from replacing
* this option's chars in the string. Use {@link #filter(MineDownParser.Option)} to completely
* remove the characters used by this option from the message instead.
* @param option The option to disable
* @return The MineDown instance
*/
public MineDown disable(MineDownParser.Option option) {
reset();
parser().disable(option);
return this;
}
/**
* Filter an option. This completely removes the characters of this option from
* the string ignoring whether the option is enabled or not.
* @param option The option to add to the filter
* @return The MineDown instance
*/
public MineDown filter(MineDownParser.Option option) {
reset();
parser().filter(option);
return this;
}
/**
* Unfilter an option. Does not enable it!
* @param option The option to remove from the filter
* @return The MineDown instance
*/
public MineDown unfilter(MineDownParser.Option option) {
reset();
parser().unfilter(option);
return this;
}
/**
* Set a special character to replace color codes by if translating legacy colors is enabled.
* @param colorChar The character to use as a special color code. (Default: ampersand &amp;)
* @return The MineDown instance
*/
public MineDown colorChar(char colorChar) {
reset();
parser().colorChar(colorChar);
return this;
}
/**
* Get the set message that is to be parsed
* @return The to be parsed message
*/
public String message() {
return this.message;
}
/**
* Set the message that is to be parsed
* @param message The message to be parsed
* @return The MineDown instance
*/
public MineDown message(String message) {
this.message = message;
reset();
return this;
}
/**
* Get the replacer instance that is currently used
* @return The currently used replacer instance
*/
public Replacer replacer() {
return this.replacer;
}
/**
* Get the parser instance that is currently used
* @return The currently used parser instance
*/
public MineDownParser parser() {
return this.parser;
}
protected BaseComponent[] baseComponents() {
return this.baseComponents;
}
/**
* Copy all MineDown settings to a new instance
* @return The new MineDown instance with all settings copied
*/
public MineDown copy() {
return new MineDown(message()).copy(this);
}
/**
* Copy all MineDown settings from another one
* @param from The MineDown to copy from
* @return This MineDown instance
*/
public MineDown copy(MineDown from) {
replacer().copy(from.replacer());
parser().copy(from.parser());
return this;
}
/**
* Get the string that represents the format in MineDown
* @param format The format
* @return The MineDown string or an empty one if it's not a format
*/
public static String getFormatString(ChatColor format) {
if (format == ChatColor.BOLD) {
return "**";
} else if (format == ChatColor.ITALIC) {
return "##";
} else if (format == ChatColor.UNDERLINE) {
return "__";
} else if (format == ChatColor.STRIKETHROUGH) {
return "~~";
} else if (format == ChatColor.MAGIC) {
return "??";
}
return "";
}
/**
* Get the ChatColor format from a MineDown string
* @param c The character
* @return The ChatColor of that format or <code>null</code> it none was found
*/
public static ChatColor getFormatFromChar(char c) {
switch (c) {
case '~':
return ChatColor.STRIKETHROUGH;
case '_':
return ChatColor.UNDERLINE;
case '*':
return ChatColor.BOLD;
case '#':
return ChatColor.ITALIC;
case '?':
return ChatColor.MAGIC;
}
return null;
}
/**
* Escape all MineDown formatting in a string. This will escape backslashes too!
* @param string The string to escape in
* @return The string with formatting escaped
*/
public static String escape(String string) {
return new MineDown(string).parser().escape(string);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,423 @@
package net.momirealms.customcrops.Libs.minedown;
/*
* Copyright (c) 2017 Max Lee (https://github.com/Phoenix616)
*
* 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.
*/
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.hover.content.Content;
import net.md_5.bungee.api.chat.hover.content.Entity;
import net.md_5.bungee.api.chat.hover.content.Item;
import net.md_5.bungee.api.chat.hover.content.Text;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import static net.momirealms.customcrops.Libs.minedown.MineDown.COLOR_PREFIX;
import static net.momirealms.customcrops.Libs.minedown.MineDown.FONT_PREFIX;
import static net.momirealms.customcrops.Libs.minedown.MineDown.FORMAT_PREFIX;
import static net.momirealms.customcrops.Libs.minedown.MineDown.HOVER_PREFIX;
import static net.momirealms.customcrops.Libs.minedown.MineDown.INSERTION_PREFIX;
public class MineDownStringifier {
private static final boolean HAS_FONT_SUPPORT = Util.hasMethod(BaseComponent.class, "getFontRaw");
private static final boolean HAS_INSERTION_SUPPORT = Util.hasMethod(BaseComponent.class, "getInsertion");
private static final boolean HAS_HOVER_CONTENT_SUPPORT = Util.hasMethod(HoverEvent.class, "getContents");
private static final Method HOVER_GET_VALUE = Util.getMethod(HoverEvent.class, "getValue");
/**
* Whether or not to use legacy color codes (Default: false)
*/
private boolean useLegacyColors = false;
/**
* Whether or not to translate legacy formatting codes over Minedown ones (Default: false)
*/
private boolean useLegacyFormatting = false;
/**
* Whether or not to use simple event definitions or specific ones (Default: true)
*/
private boolean preferSimpleEvents = true;
/**
* Whether or not to put formatting in event definitions (Default: false)
*/
private boolean formattingInEventDefinition = false;
/**
* Whether or not to put colors in event definitions (Default: true)
*/
private boolean colorInEventDefinition = true;
/**
* The character to use as a special color code. (Default: ampersand &amp;)
*/
private char colorChar = '&';
private StringBuilder value = new StringBuilder();
private ChatColor color = null;
private ClickEvent clickEvent = null;
private HoverEvent hoverEvent = null;
private Set<ChatColor> formats = new LinkedHashSet<>();
/**
* Create a {@link MineDown} string from a component message
* @param components The components to generate a MineDown string from
* @return The MineDown string
*/
public String stringify(BaseComponent... components) {
StringBuilder sb = new StringBuilder();
for (BaseComponent component : components) {
if (!component.hasFormatting()) {
appendText(sb, component);
continue;
}
boolean hasEvent = component.getFontRaw() != null || component.getInsertion() != null
|| component.getClickEvent() != clickEvent || component.getHoverEvent() != hoverEvent;
if (hasEvent) {
sb.append('[');
if (!formattingInEventDefinition()) {
appendFormat(sb, component);
}
if (!colorInEventDefinition()) {
appendColor(sb, component.getColor());
}
} else if (component.getColorRaw() != null) {
appendFormat(sb, component);
appendColor(sb, component.getColor());
} else {
appendFormat(sb, component);
}
appendText(sb, component);
if (component.getExtra() != null && !component.getExtra().isEmpty()) {
sb.append(copy().stringify(component.getExtra().toArray(new BaseComponent[0])));
}
if (hasEvent) {
clickEvent = component.getClickEvent();
hoverEvent = component.getHoverEvent();
if (!formattingInEventDefinition()) {
appendFormatSuffix(sb, component);
}
sb.append("](");
List<String> definitions = new ArrayList<>();
if (colorInEventDefinition()) {
StringBuilder sbi = new StringBuilder();
if (!preferSimpleEvents()) {
sbi.append(COLOR_PREFIX);
}
sbi.append(component.getColor().getName().toLowerCase(Locale.ROOT));
definitions.add(sbi.toString());
}
if (formattingInEventDefinition()) {
StringBuilder sbi = new StringBuilder();
if (!preferSimpleEvents) {
sbi.append(FORMAT_PREFIX);
}
sbi.append(Util.getFormats(component, true).stream().map(c -> c.getName().toLowerCase(Locale.ROOT)).collect(Collectors.joining(" ")));
definitions.add(sbi.toString());
}
if (HAS_FONT_SUPPORT && component.getFontRaw() != null) {
definitions.add(FONT_PREFIX + component.getFontRaw());
}
if (HAS_INSERTION_SUPPORT && component.getInsertion() != null) {
if (component.getInsertion().contains(" ")) {
definitions.add(INSERTION_PREFIX + "{" + component.getInsertion() + "}");
} else {
definitions.add(INSERTION_PREFIX + component.getInsertion());
}
}
if (component.getClickEvent() != null) {
if (preferSimpleEvents() && component.getClickEvent().getAction() == ClickEvent.Action.OPEN_URL) {
definitions.add(component.getClickEvent().getValue());
} else {
definitions.add(component.getClickEvent().getAction().toString().toLowerCase(Locale.ROOT) + "=" + component.getClickEvent().getValue());
}
}
if (component.getHoverEvent() != null) {
StringBuilder sbi = new StringBuilder();
if (preferSimpleEvents() && component.getHoverEvent().getAction() == HoverEvent.Action.SHOW_TEXT &&
(component.getClickEvent() == null || component.getClickEvent().getAction() != ClickEvent.Action.OPEN_URL)) {
sbi.append(HOVER_PREFIX);
} else {
sbi.append(component.getHoverEvent().getAction().toString().toLowerCase(Locale.ROOT)).append('=');
}
if (HAS_HOVER_CONTENT_SUPPORT) {
sbi.append(copy().stringify(component.getHoverEvent().getContents()));
} else if (HOVER_GET_VALUE != null) {
try {
sbi.append(copy().stringify((BaseComponent[]) HOVER_GET_VALUE.invoke(component.getHoverEvent())));
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
definitions.add(sbi.toString());
}
sb.append(definitions.stream().collect(Collectors.joining(" ")));
sb.append(')');
} else {
appendFormatSuffix(sb, component);
}
}
return sb.toString();
}
private StringBuilder stringify(List<Content> contents) {
StringBuilder sb = new StringBuilder();
for (Content content : contents) {
if (content instanceof Text) {
Object value = ((Text) content).getValue();
if (value instanceof BaseComponent[]) {
sb.append(stringify((BaseComponent[]) value));
} else {
sb.append(value);
}
} else if (content instanceof Entity) {
Entity contentEntity = (Entity) content;
sb.append(contentEntity.getId());
if (contentEntity.getType() != null) {
sb.append(":").append(contentEntity.getType());
}
if (contentEntity.getName() != null) {
sb.append(" ").append(stringify(contentEntity.getName()));
}
} else if (content instanceof Item) {
Item contentItem = (Item) content;
sb.append(contentItem.getId());
if (contentItem.getCount() > 0) {
sb.append("*").append(contentItem.getCount());
}
if (contentItem.getTag() != null) {
sb.append(" ").append(contentItem.getTag().getNbt());
}
}
}
return sb;
}
private void appendText(StringBuilder sb, BaseComponent component) {
if (component instanceof TextComponent) {
sb.append(((TextComponent) component).getText());
} else {
throw new UnsupportedOperationException("Cannot stringify " + component.getClass().getTypeName() + " yet! Only TextComponents are supported right now. Sorry. :(");
}
}
private void appendColor(StringBuilder sb, ChatColor color) {
if (this.color != color) {
this.color = color;
if (useLegacyColors()) {
sb.append(colorChar()).append(color.toString().substring(1));
} else {
sb.append(colorChar()).append(color.getName()).append(colorChar());
}
}
}
private void appendFormat(StringBuilder sb, BaseComponent component) {
Set<ChatColor> formats = Util.getFormats(component, true);
if (!formats.containsAll(this.formats)) {
if (useLegacyFormatting()) {
sb.append(colorChar()).append(ChatColor.RESET.toString().charAt(1));
} else {
Deque<ChatColor> formatDeque = new ArrayDeque<>(this.formats);
while (!formatDeque.isEmpty()) {
ChatColor format = formatDeque.pollLast();
if (!formats.contains(format)) {
sb.append(MineDown.getFormatString(format));
}
}
}
} else {
formats.removeAll(this.formats);
}
for (ChatColor format : formats) {
if (useLegacyFormatting()) {
sb.append(colorChar()).append(format.toString().charAt(1));
} else {
sb.append(MineDown.getFormatString(format));
}
}
this.formats.clear();
this.formats.addAll(formats);
}
private void appendFormatSuffix(StringBuilder sb, BaseComponent component) {
if (!useLegacyFormatting()) {
Set<ChatColor> formats = Util.getFormats(component, true);
for (ChatColor format : formats) {
sb.append(MineDown.getFormatString(format));
}
this.formats.removeAll(formats);
}
}
/**
* Copy all the parser's setting to a new instance
* @return The new parser instance with all settings copied
*/
public MineDownStringifier copy() {
return new MineDownStringifier().copy(this);
}
/**
* Copy all the parser's settings from another parser
* @param from The stringifier to copy from
* @return This stringifier's instance
*/
public MineDownStringifier copy(MineDownStringifier from) {
MineDownStringifier copy = new MineDownStringifier();
useLegacyColors(from.useLegacyColors());
useLegacyFormatting(from.useLegacyFormatting());
preferSimpleEvents(from.preferSimpleEvents());
formattingInEventDefinition(from.formattingInEventDefinition());
colorInEventDefinition(from.colorInEventDefinition());
colorChar(from.colorChar());
return copy;
}
/**
* Get whether or not to use legacy color codes
* @return whether or not to use legacy color codes when possible (Default: true)
*/
public boolean useLegacyColors() {
return this.useLegacyColors;
}
/**
* Set whether or not to use legacy color codes
* @param useLegacyColors Whether or not to use legacy colors (Default: true)
* @return The MineDownStringifier instance
*/
public MineDownStringifier useLegacyColors(boolean useLegacyColors) {
this.useLegacyColors = useLegacyColors;
return this;
}
/**
* Get whether or not to translate legacy formatting codes over MineDown ones
* @return whether or not to use legacy formatting codes (Default: false)
*/
public boolean useLegacyFormatting() {
return this.useLegacyFormatting;
}
/**
* Set whether or not to translate legacy formatting codes over MineDown ones
* @param useLegacyFormatting Whether or not to translate legacy formatting codes (Default: false)
* @return The MineDownStringifier instance
*/
public MineDownStringifier useLegacyFormatting(boolean useLegacyFormatting) {
this.useLegacyFormatting = useLegacyFormatting;
return this;
}
/**
* Get whether or not to use simple event definitions or specific ones (Default: true)
* @return whether or not to use simple events
*/
public boolean preferSimpleEvents() {
return this.preferSimpleEvents;
}
/**
* Set whether or not to use simple event definitions or specific ones
* @param preferSimpleEvents Whether or not to prefer simple events (Default: true)
* @return The MineDownStringifier instance
*/
public MineDownStringifier preferSimpleEvents(boolean preferSimpleEvents) {
this.preferSimpleEvents = preferSimpleEvents;
return this;
}
/**
* Get whether or not to put colors in event definitions or use inline color definitions
* @return whether or not to put colors in event definitions (Default: false)
*/
public boolean colorInEventDefinition() {
return this.colorInEventDefinition;
}
/**
* Set whether or not to put colors in event definitions or use inline color definitions
* @param colorInEventDefinition Whether or not to put colors in event definitions (Default: false)
* @return The MineDownStringifier instance
*/
public MineDownStringifier colorInEventDefinition(boolean colorInEventDefinition) {
this.colorInEventDefinition = colorInEventDefinition;
return this;
}
/**
* Get whether or not to put formatting in event definitions or use inline formatting definitions
* @return whether or not to put formatting in event definitions (Default: false)
*/
public boolean formattingInEventDefinition() {
return this.formattingInEventDefinition;
}
/**
* Set whether or not to put formatting in event definitions or use inline formatting definitions
* @param formattingInEventDefinition Whether or not to put formatting in event definitions (Default: false)
* @return The MineDownStringifier instance
*/
public MineDownStringifier formattingInEventDefinition(boolean formattingInEventDefinition) {
this.formattingInEventDefinition = formattingInEventDefinition;
return this;
}
/**
* Get the character to use as a special color code. (Default: ampersand &amp;)
* @return the color character
*/
public char colorChar() {
return this.colorChar;
}
/**
* Set the character to use as a special color code.
* @param colorChar The character to be used as the color char (for legacy and MineDown colors, default: ampersand &amp;)
* @return The MineDownStringifier instance
*/
public MineDownStringifier colorChar(char colorChar) {
this.colorChar = colorChar;
return this;
}
}

View File

@@ -0,0 +1,471 @@
package net.momirealms.customcrops.Libs.minedown;
/*
* Copyright (c) 2017 Max Lee (https://github.com/Phoenix616)
*
* 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.
*/
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.ItemTag;
import net.md_5.bungee.api.chat.KeybindComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import net.md_5.bungee.api.chat.hover.content.Content;
import net.md_5.bungee.api.chat.hover.content.Entity;
import net.md_5.bungee.api.chat.hover.content.Item;
import net.md_5.bungee.api.chat.hover.content.Text;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class offers the ability to replace placeholders with values in strings and components.
* It also lets you define which placeholders indicators (prefix and suffix) should be used.
* By default these are the % character.
*/
public class Replacer {
private static final boolean HAS_KEYBIND_SUPPORT = Util.hasClass("net.md_5.bungee.api.chat.KeybindComponent");
private static final boolean HAS_INSERTION_SUPPORT = Util.hasMethod(BaseComponent.class, "getInsertion");
private static final boolean HAS_HOVER_CONTENT_SUPPORT = Util.hasMethod(HoverEvent.class, "getContents");
private static final Method HOVER_GET_VALUE = Util.getMethod(HoverEvent.class, "getValue");
/**
* A cache of compiled replacement patterns
*/
private static final Map<String, Pattern> PATTERN_CACHE = new ConcurrentHashMap<>();
/**
* The creator of the patterns for the pattern cache
*/
private static final Function<String, Pattern> PATTERN_CREATOR = p -> Pattern.compile(p, Pattern.LITERAL);
/**
* The map of placeholders with their string replacements
*/
private final Map<String, String> replacements = new LinkedHashMap<>();
/**
* The map of placeholders with their component array replacements
*/
private final Map<String, BaseComponent[]> componentReplacements = new LinkedHashMap<>();
/**
* The placeholder indicator's prefix character
*/
private String placeholderPrefix = "%";
/**
* The placeholder indicator's suffix character
*/
private String placeholderSuffix = "%";
/**
* Replace the placeholder no matter what the case of it is
*/
private boolean ignorePlaceholderCase = true;
/**
* Replace certain placeholders with values in string.
* This uses the % character as placeholder indicators (suffix and prefix)
* @param message The string to replace in
* @param replacements The replacements, nth element is the placeholder, n+1th the value
* @return The string with all the placeholders replaced
*/
public static String replaceIn(String message, String... replacements) {
return new Replacer().replace(replacements).replaceIn(message);
}
/**
* Replace certain placeholders with values in a component array.
* This uses the % character as placeholder indicators (suffix and prefix)
* @param message The BaseComponent array to replace in
* @param replacements The replacements, nth element is the placeholder, n+1th the value
* @return A copy of the BaseComponent array with all the placeholders replaced
*/
public static BaseComponent[] replaceIn(BaseComponent[] message, String... replacements) {
return new Replacer().replace(replacements).replaceIn(message);
}
/**
* Replace a certain placeholder with a component array in a component array.
* This uses the % character as placeholder indicators (suffix and prefix)
* @param message The BaseComponent array to replace in
* @param placeholder The placeholder to replace
* @param replacement The replacement components
* @return A copy of the BaseComponent array with all the placeholders replaced
*/
public static BaseComponent[] replaceIn(BaseComponent[] message, String placeholder, BaseComponent... replacement) {
return new Replacer().replace(placeholder, replacement).replaceIn(message);
}
/**
* Add an array with placeholders and values that should get replaced in the message
* @param replacements The replacements, nth element is the placeholder, n+1th the value
* @return The Replacer instance
*/
public Replacer replace(String... replacements) {
Util.validate(replacements.length % 2 == 0, "The replacement length has to be even, " +
"mapping i % 2 == 0 to the placeholder and i % 2 = 1 to the placeholder's value");
Map<String, String> replacementMap = new LinkedHashMap<>();
for (int i = 0; i + 1 < replacements.length; i += 2) {
replacementMap.put(replacements[i], replacements[i + 1]);
}
return replace(replacementMap);
}
/**
* Add a map with placeholders and values that should get replaced in the message
* @param replacements The replacements mapped placeholder to value
* @return The Replacer instance
*/
public Replacer replace(Map<String, ?> replacements) {
if (replacements != null && !replacements.isEmpty()) {
Object any = replacements.values().stream().filter(Objects::nonNull).findAny().orElse(null);
if (any instanceof String) {
replacements().putAll((Map<String, String>) replacements);
} else if (any != null && any.getClass().isArray() && BaseComponent.class.isAssignableFrom(any.getClass().getComponentType())) {
componentReplacements().putAll((Map<String, BaseComponent[]>) replacements);
} else {
for (Map.Entry<String, ?> entry : replacements.entrySet()) {
replacements().put(entry.getKey(), String.valueOf(entry.getValue()));
}
}
}
return this;
}
/**
* Add a placeholder to component mapping that should get replaced in the message
* @param placeholder The placeholder to replace
* @param replacement The replacement components
* @return The Replacer instance
*/
public Replacer replace(String placeholder, BaseComponent... replacement) {
componentReplacements().put(placeholder, replacement);
return this;
}
/**
* Set the placeholder indicator for both prefix and suffix
* @param placeholderIndicator The character to use as a placeholder indicator
* @return The Replacer instance
*/
public Replacer placeholderIndicator(String placeholderIndicator) {
placeholderPrefix(placeholderIndicator);
placeholderSuffix(placeholderIndicator);
return this;
}
/**
* Replace the placeholders in a component array
* @param components The BaseComponent array to replace in
* @return A copy of the array with the placeholders replaced
*/
public BaseComponent[] replaceIn(BaseComponent... components) {
return replaceIn(Arrays.asList(components));
}
/**
* Replace the placeholders in a component list
* @param components The BaseComponent list to replace in
* @return A copy of the array with the placeholders replaced
*/
public BaseComponent[] replaceIn(List<BaseComponent> components) {
List<BaseComponent> returnList = new ArrayList<>();
// String replacements:
for (int i = 0; i < components.size(); i++) {
BaseComponent component = components.get(i).duplicate();
if (HAS_KEYBIND_SUPPORT && component instanceof KeybindComponent) {
((KeybindComponent) component).setKeybind(replaceIn(((KeybindComponent) component).getKeybind()));
}
if (component instanceof TextComponent) {
String replaced = replaceIn(((TextComponent) component).getText());
int sectionIndex = replaced.indexOf(ChatColor.COLOR_CHAR);
if (sectionIndex > -1 && replaced.length() > sectionIndex + 1
&& ChatColor.getByChar(replaced.charAt(sectionIndex + 1)) != null) {
// replacement contain legacy code, parse to components and append them as extra
BaseComponent[] replacedComponent = TextComponent.fromLegacyText(replaced);
((TextComponent) component).setText("");
List<BaseComponent> extra = new ArrayList<>();
Collections.addAll(extra, replacedComponent);
if (component.getExtra() != null) {
Collections.addAll(extra, replaceIn(component.getExtra()));
}
component.setExtra(extra);
} else {
((TextComponent) component).setText(replaced);
if (component.getExtra() != null) {
component.setExtra(Arrays.asList(replaceIn(component.getExtra())));
}
}
} else if (component.getExtra() != null) {
component.setExtra(Arrays.asList(replaceIn(component.getExtra())));
}
if (component instanceof TranslatableComponent) {
((TranslatableComponent) component).setTranslate(replaceIn(((TranslatableComponent) component).getTranslate()));
((TranslatableComponent) component).setWith(Arrays.asList(replaceIn(((TranslatableComponent) component).getWith())));
}
if (HAS_INSERTION_SUPPORT && component.getInsertion() != null) {
component.setInsertion(replaceIn(component.getInsertion()));
}
if (component.getClickEvent() != null) {
component.setClickEvent(new ClickEvent(
component.getClickEvent().getAction(),
replaceIn(component.getClickEvent().getValue())
));
}
if (component.getHoverEvent() != null) {
if (HAS_HOVER_CONTENT_SUPPORT) {
component.setHoverEvent(new HoverEvent(
component.getHoverEvent().getAction(),
replaceInContents(component.getHoverEvent().getContents())
));
} else if (HOVER_GET_VALUE != null) {
try {
component.setHoverEvent(new HoverEvent(
component.getHoverEvent().getAction(),
replaceIn((BaseComponent[]) HOVER_GET_VALUE.invoke(component.getHoverEvent()))
));
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
// Component replacements
List<BaseComponent> replacedComponents = new ArrayList<>();
replacedComponents.add(component);
for (Map.Entry<String, BaseComponent[]> replacement : componentReplacements().entrySet()) {
List<BaseComponent> newReplacedComponents = new ArrayList<>();
for (BaseComponent replaceComponent : replacedComponents) {
if (replaceComponent instanceof TextComponent) {
TextComponent textComponent = (TextComponent) replaceComponent;
String placeHolder = placeholderPrefix()
+ (ignorePlaceholderCase() ? replacement.getKey().toLowerCase(Locale.ROOT) : replacement.getKey())
+ placeholderSuffix();
String text = ignorePlaceholderCase() ? textComponent.getText().toLowerCase(Locale.ROOT) : textComponent.getText();
int index = text.indexOf(placeHolder);
if (index > -1) {
do {
TextComponent startComponent = new TextComponent(textComponent);
if (index > 0) {
startComponent.setText(textComponent.getText().substring(0, index));
} else {
startComponent.setText("");
}
startComponent.setExtra(Arrays.asList(replacement.getValue()));
newReplacedComponents.add(startComponent);
if (index + placeHolder.length() < textComponent.getText().length()) {
textComponent.setText(textComponent.getText().substring(index + placeHolder.length()));
} else {
textComponent.setText("");
}
text = ignorePlaceholderCase() ? textComponent.getText().toLowerCase(Locale.ROOT) : textComponent.getText();
newReplacedComponents.add(textComponent);
} while (!text.isEmpty() && (index = text.indexOf(placeHolder)) > -1);
continue;
}
}
// Nothing was replaced, just add it
newReplacedComponents.add(replaceComponent);
}
replacedComponents = newReplacedComponents;
}
returnList.addAll(replacedComponents);
}
return returnList.toArray(new BaseComponent[0]);
}
private List<Content> replaceInContents(List<Content> contents) {
List<Content> replacedContents = new ArrayList<>();
for (Content content : contents) {
if (content instanceof Text) {
Object value = ((Text) content).getValue();
if (value instanceof BaseComponent[]) {
replacedContents.add(new Text(replaceIn((BaseComponent[]) value)));
} else if (value instanceof String) {
replacedContents.add(new Text(replaceIn((String) value)));
} else {
throw new UnsupportedOperationException("Cannot replace in " + value.getClass() + "!");
}
} else if (content instanceof Entity) {
Entity entity = (Entity) content;
String id = replaceIn(entity.getId());
String type;
if (entity.getType() != null) {
type = replaceIn(entity.getType());
} else {
type = "minecraft:pig"; // Meh
}
BaseComponent name = null;
if (entity.getName() != null) {
name = new TextComponent(replaceIn(TextComponent.toLegacyText(entity.getName())));
}
replacedContents.add(new Entity(type, id, name));
} else if (content instanceof Item) {
Item item = (Item) content;
String id = replaceIn(item.getId());
ItemTag itemTag = item.getTag() != null ? ItemTag.ofNbt(replaceIn(item.getTag().getNbt())) : null;
replacedContents.add(new Item(id, item.getCount(), itemTag));
} else {
replacedContents.add(content); // TODO: Find a good way to clone this
}
}
return replacedContents;
}
/**
* Replace the placeholders in a string. Does not replace component replacements!
* @param string The String list to replace in
* @return The string with the placeholders replaced
*/
public String replaceIn(String string) {
for (Map.Entry<String, String> replacement : replacements().entrySet()) {
String replValue = replacement.getValue() != null ? replacement.getValue() : "null";
if (ignorePlaceholderCase()) {
String placeholder = placeholderPrefix() + replacement.getKey().toLowerCase(Locale.ROOT) + placeholderSuffix();
int nextStart = 0;
int startIndex;
while (nextStart < string.length() && (startIndex = string.toLowerCase(Locale.ROOT).indexOf(placeholder, nextStart)) > -1) {
nextStart = startIndex + replValue.length();
string = string.substring(0, startIndex) + replValue + string.substring(startIndex + placeholder.length());
}
} else {
String placeholder = placeholderPrefix() + replacement.getKey() + placeholderSuffix();
Pattern pattern = PATTERN_CACHE.computeIfAbsent(placeholder, PATTERN_CREATOR);
string = pattern.matcher(string).replaceAll(Matcher.quoteReplacement(replValue));
}
}
return string;
}
/**
* Create a copy of this Replacer
* @return A copy of this Replacer
*/
public Replacer copy() {
return new Replacer().copy(this);
}
/**
* Copy all the values of another Replacer
* @param from The replacer to copy
* @return The Replacer instance
*/
public Replacer copy(Replacer from) {
replacements().clear();
replacements().putAll(from.replacements());
componentReplacements().clear();
componentReplacements().putAll(from.componentReplacements());
placeholderPrefix(from.placeholderPrefix());
placeholderSuffix(from.placeholderSuffix());
return this;
}
/**
* Get the map of placeholders with their string replacements
* @return the replacement map
*/
public Map<String, String> replacements() {
return this.replacements;
}
/**
* Get the map of placeholders with their component array replacements
* @return the replacement map
*/
public Map<String, BaseComponent[]> componentReplacements() {
return this.componentReplacements;
}
/**
* Get the placeholder indicator's prefix string
* @return the prefix characters
*/
public String placeholderPrefix() {
return this.placeholderPrefix;
}
/**
* Set the placeholder indicator's prefix string
* @param placeholderPrefix The placeholder prefix string
* @return the instance of this Replacer
*/
public Replacer placeholderPrefix(String placeholderPrefix) {
this.placeholderPrefix = placeholderPrefix;
return this;
}
/**
* Get the placeholder indicator's suffix string
* @return the suffix characters
*/
public String placeholderSuffix() {
return this.placeholderSuffix;
}
/**
* Set the placeholder indicator's suffix string
* @param placeholderSuffix The placeholder suffix string
* @return the instance of this Replacer
*/
public Replacer placeholderSuffix(String placeholderSuffix) {
this.placeholderSuffix = placeholderSuffix;
return this;
}
/**
* Replace the placeholder no matter what the case of it is
* @return whether or not to ignore the placeholder case (Default: true)
*/
public boolean ignorePlaceholderCase() {
return this.ignorePlaceholderCase;
}
/**
* Set whether or not the placeholder should be replaced no matter what the case of it is
* @param ignorePlaceholderCase Whether or not to ignore the case in placeholders (Default: true)
* @return the instance of this Replacer
*/
public Replacer ignorePlaceholderCase(boolean ignorePlaceholderCase) {
this.ignorePlaceholderCase = ignorePlaceholderCase;
return this;
}
}

View File

@@ -0,0 +1,590 @@
package net.momirealms.customcrops.Libs.minedown;
/*
* Copyright (c) 2017 Max Lee (https://github.com/Phoenix616)
*
* 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.
*/
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import java.awt.Color;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public class Util {
private static final Pattern WRAP_PATTERN = Pattern.compile(" ", Pattern.LITERAL);
/**
* Utility method to throw an IllegalArgumentException if the value is false
* @param value The value to validate
* @param message The message for the exception
* @throws IllegalArgumentException Thrown if the value is false
*/
public static void validate(boolean value, String message) throws IllegalArgumentException {
if (!value) {
throw new IllegalArgumentException(message);
}
}
/**
* Apply a collection of colors/formats to a component
* @param component The BaseComponent
* @param formats The collection of ChatColor formats to apply
* @return The component that was modified
*/
public static BaseComponent applyFormat(BaseComponent component, Collection<ChatColor> formats) {
for (ChatColor format : formats) {
if (format == ChatColor.BOLD) {
component.setBold(true);
} else if (format == ChatColor.ITALIC) {
component.setItalic(true);
} else if (format == ChatColor.UNDERLINE) {
component.setUnderlined(true);
} else if (format == ChatColor.STRIKETHROUGH) {
component.setStrikethrough(true);
} else if (format == ChatColor.MAGIC) {
component.setObfuscated(true);
} else if (format == ChatColor.RESET) {
component.setBold(false);
component.setItalic(false);
component.setUnderlined(false);
component.setStrikethrough(false);
component.setObfuscated(false);
component.setColor(ChatColor.WHITE);
} else {
component.setColor(format);
}
}
if (component.getExtra() != null) {
for (BaseComponent extra : component.getExtra()) {
applyFormat(extra, formats);
}
}
return component;
}
/**
* Apply a collection of colors/formats to a component builder
* @param builder The ComponentBuilder
* @param formats The collection of ChatColor formats to apply
* @return The component builder that was modified
* @deprecated Use {@link #applyFormat(BaseComponent, Collection)}
*/
@Deprecated
public static ComponentBuilder applyFormat(ComponentBuilder builder, Set<ChatColor> formats) {
Map<ChatColor, Boolean> formatMap = new HashMap<>();
for (ChatColor format : formats) {
formatMap.put(format, true);
}
return applyFormat(builder, formatMap);
}
/**
* Apply a collection of colors/formats to a component builder
* @param builder The ComponentBuilder
* @param formats The collection of ChatColor formats to apply
* @return The component builder that was modified
*/
public static ComponentBuilder applyFormat(ComponentBuilder builder, Map<ChatColor, Boolean> formats) {
for (Map.Entry<ChatColor, Boolean> e : formats.entrySet()) {
if (e.getKey() == ChatColor.BOLD) {
builder.bold(e.getValue());
} else if (e.getKey() == ChatColor.ITALIC) {
builder.italic(e.getValue());
} else if (e.getKey() == ChatColor.UNDERLINE) {
builder.underlined(e.getValue());
} else if (e.getKey() == ChatColor.STRIKETHROUGH) {
builder.strikethrough(e.getValue());
} else if (e.getKey() == ChatColor.MAGIC) {
builder.obfuscated(e.getValue());
} else if (e.getKey() == ChatColor.RESET) {
builder.bold(!e.getValue());
builder.italic(!e.getValue());
builder.underlined(!e.getValue());
builder.strikethrough(!e.getValue());
builder.obfuscated(!e.getValue());
builder.color(ChatColor.WHITE);
} else if (e.getValue()) {
builder.color(e.getKey());
} else if (builder.getCurrentComponent().getColor() == e.getKey()) {
builder.color(null);
}
}
return builder;
}
/**
* Check whether or not a character at a certain index of a string repeats itself
* @param string The string to check
* @param index The index at which to check the character
* @return Whether or not the character at that index repeated itself
*/
public static boolean isDouble(String string, int index) {
return index + 1 < string.length() && string.charAt(index) == string.charAt(index + 1);
}
/**
* Check whether a certain ChatColor is formatting or not
* @param format The ChatColor to check
* @return <code>true</code> if it's a format, <code>false</code> if it's a color
*/
public static boolean isFormat(ChatColor format) {
return !MineDown.getFormatString(format).isEmpty();
}
/**
* Get a set of ChatColor formats all formats that a component includes
* @param component The component to get the formats from
* @param ignoreParent Whether or not to include the parent's format
* @return A set of all the format ChatColors that the component includes
*/
public static Set<ChatColor> getFormats(BaseComponent component, boolean ignoreParent) {
Set<ChatColor> formats = new LinkedHashSet<>();
if ((!ignoreParent && component.isBold()) || (component.isBoldRaw() != null && component.isBoldRaw())) {
formats.add(ChatColor.BOLD);
}
if ((!ignoreParent && component.isItalic()) || (component.isItalicRaw() != null && component.isItalicRaw())) {
formats.add(ChatColor.ITALIC);
}
if ((!ignoreParent && component.isUnderlined()) || (component.isUnderlinedRaw() != null && component.isUnderlinedRaw())) {
formats.add(ChatColor.UNDERLINE);
}
if ((!ignoreParent && component.isStrikethrough()) || (component.isStrikethroughRaw() != null && component.isStrikethroughRaw())) {
formats.add(ChatColor.STRIKETHROUGH);
}
if ((!ignoreParent && component.isObfuscated()) || (component.isObfuscatedRaw() != null && component.isObfuscatedRaw())) {
formats.add(ChatColor.MAGIC);
}
return formats;
}
/**
* Get the index of the first occurrences of a not escaped character
* @param string The string to search
* @param chars The characters to search for
* @return The first unescaped index or -1 if not found
*/
public static int indexOfNotEscaped(String string, String chars) {
return indexOfNotEscaped(string, chars, 0);
}
/**
* Get the index of the first occurrences of a not escaped character
* @param string The string to search
* @param chars The characters to search for
* @param fromIndex Start searching from that index
* @return The first unescaped index or {@code -1} if not found
*/
public static int indexOfNotEscaped(String string, String chars, int fromIndex) {
for (int i = fromIndex; i < string.length(); i++) {
int index = string.indexOf(chars, i);
if (index == -1) {
return -1;
}
if (!isEscaped(string, index)) {
return index;
}
}
return -1;
}
/**
* Check if a character at a certain index is escaped
* @param string The string to check
* @param index The index of the character in the string to check
* @return Whether or not the character is escaped (uneven number of backslashes in front of char mains it is escaped)
* @throws IndexOutOfBoundsException if the {@code index} argument is not less than the length of this string.
*/
public static boolean isEscaped(String string, int index) {
if (index - 1 > string.length()) {
return false;
}
int e = 0;
while (index > e && string.charAt(index - e - 1) == '\\') {
e++;
}
return e % 2 != 0;
}
/**
* Wrap a string if it is longer than the line length and contains no new line.
* Will try to wrap at spaces between words.
* @param string The string to wrap
* @param lineLength The max length of a line
* @return The wrapped string
*/
public static String wrap(String string, int lineLength) {
if (string.length() <= lineLength || string.contains("\n")) {
return string;
}
List<String> lines = new ArrayList<>();
StringBuilder currentLine = new StringBuilder();
for (String s : WRAP_PATTERN.split(string)) {
if (currentLine.length() + s.length() + 1 > lineLength) {
int rest = lineLength - currentLine.length() - 1;
if (rest > lineLength / 4 && s.length() > Math.min(rest * 2, lineLength / 4)) {
currentLine.append(" ").append(s, 0, rest);
} else {
rest = 0;
}
lines.add(currentLine.toString());
String restString = s.substring(rest);
while (restString.length() >= lineLength) {
lines.add(restString.substring(0, lineLength));
restString = restString.substring(lineLength);
}
currentLine = new StringBuilder(restString);
} else {
if (currentLine.length() > 0) {
currentLine.append(" ");
}
currentLine.append(s);
}
}
if (currentLine.length() > 0) {
lines.add(currentLine.toString());
}
return String.join("\n", lines);
}
private static Map<ChatColor, Color> legacyColors = new LinkedHashMap<>();
static {
legacyColors.put(ChatColor.BLACK, new Color(0x000000));
legacyColors.put(ChatColor.DARK_BLUE, new Color(0x0000AA));
legacyColors.put(ChatColor.DARK_GREEN, new Color(0x00AA00));
legacyColors.put(ChatColor.DARK_AQUA, new Color(0x00AAAA));
legacyColors.put(ChatColor.DARK_RED, new Color(0xAA0000));
legacyColors.put(ChatColor.DARK_PURPLE, new Color(0xAA00AA));
legacyColors.put(ChatColor.GOLD, new Color(0xFFAA00));
legacyColors.put(ChatColor.GRAY, new Color(0xAAAAAA));
legacyColors.put(ChatColor.DARK_GRAY, new Color(0x555555));
legacyColors.put(ChatColor.BLUE, new Color(0x05555FF));
legacyColors.put(ChatColor.GREEN, new Color(0x55FF55));
legacyColors.put(ChatColor.AQUA, new Color(0x55FFFF));
legacyColors.put(ChatColor.RED, new Color(0xFF5555));
legacyColors.put(ChatColor.LIGHT_PURPLE, new Color(0xFF55FF));
legacyColors.put(ChatColor.YELLOW, new Color(0xFFFF55));
legacyColors.put(ChatColor.WHITE, new Color(0xFFFFFF));
}
/**
* Utility method to remove RGB colors from components. This modifies the input array!
* @param components The components to remove the rgb colors from
* @return The modified components (same as input).
*/
public static BaseComponent[] rgbColorsToLegacy(BaseComponent[] components) {
for (BaseComponent component : components) {
if (component.getColorRaw() != null && component.getColorRaw().getName().startsWith("#")) {
component.setColor(getClosestLegacy(new Color(Integer.parseInt(component.getColorRaw().getName().substring(1), 16))));
}
if (component.getExtra() != null) {
rgbColorsToLegacy(component.getExtra().toArray(new BaseComponent[0]));
}
}
return components;
}
/**
* Get the legacy color closest to a certain RGB color
* @param color The color to get the closest legacy color for
* @return The closest legacy color
*/
public static ChatColor getClosestLegacy(Color color) {
ChatColor closest = null;
double smallestDistance = Double.MAX_VALUE;
for (Map.Entry<ChatColor, Color> legacy : legacyColors.entrySet()) {
double distance = distance(color, legacy.getValue());
if (distance < smallestDistance) {
smallestDistance = distance;
closest = legacy.getKey();
}
}
return closest;
}
/**
* Get the distance between two colors
* @param c1 Color A
* @param c2 Color B
* @return The distance or 0 if they are equal
*/
public static double distance(Color c1, Color c2) {
if (c1.getRGB() == c2.getRGB()) {
return 0;
}
return Math.sqrt(Math.pow(c1.getRed() - c2.getRed(), 2) + Math.pow(c1.getGreen() - c2.getGreen(), 2) + Math.pow(c1.getBlue() - c2.getBlue(), 2));
}
/*
* createRainbow is adapted from the net.kyori.adventure.text.minimessage.fancy.Rainbow class
* in adventure-text-minimessage, licensed under the MIT License.
*
* Copyright (c) 2018-2020 KyoriPowered
*
* 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.
*/
/**
* Generate a rainbow with a certain length and phase
* @param length The length of the rainbow
* @param phase The phase of the rainbow.
* @param rgb Whether or not to use RGB colors
* @return the colors in the rainbow
* @deprecated Use {@link #createRainbow(long, int, boolean)}
*/
@Deprecated
public static List<ChatColor> createRainbow(int length, int phase, boolean rgb) {
return createRainbow((long) length, phase, rgb);
}
/**
* Generate a rainbow with a certain length and phase
* @param length The length of the rainbow
* @param phase The phase of the rainbow.
* @param rgb Whether or not to use RGB colors
* @return the colors in the rainbow
*/
public static List<ChatColor> createRainbow(long length, int phase, boolean rgb) {
List<ChatColor> colors = new ArrayList<>();
float fPhase = phase / 10f;
float center = 128;
float width = 127;
double frequency = Math.PI * 2 / length;
for (int i = 0; i < length; i++) {
Color color = new Color(
(int) (Math.sin(frequency * i + 2 + fPhase) * width + center),
(int) (Math.sin(frequency * i + 0 + fPhase) * width + center),
(int) (Math.sin(frequency * i + 4 + fPhase) * width + center)
);
if (rgb) {
colors.add(ChatColor.of(color));
} else {
ChatColor chatColor = getClosestLegacy(color);
if (colors.isEmpty() || chatColor != colors.get(colors.size() - 1)) {
colors.add(chatColor);
}
}
}
return colors;
}
/*
* createGradient is adapted from the net.kyori.adventure.text.minimessage.fancy.Gradient class
* in adventure-text-minimessage, licensed under the MIT License.
*
* Copyright (c) 2018-2020 KyoriPowered
*
* 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.
*/
/**
* Generate a gradient with certain colors
* @param length The length of the gradient
* @param gradient The colors of the gradient.
* @param rgb Whether or not to use RGB colors
* @return the colors in the gradient
* @deprecated Use {@link #createRainbow(long, int, boolean)}
*/
@Deprecated
public static List<ChatColor> createGradient(int length, List<ChatColor> gradient, boolean rgb) {
return createGradient((long) length, gradient, rgb);
}
/**
* Generate a gradient with certain colors
* @param length The length of the gradient
* @param gradient The colors of the gradient.
* @param rgb Whether or not to use RGB colors
* @return the colors in the gradient
*/
public static List<ChatColor> createGradient(long length, List<ChatColor> gradient, boolean rgb) {
List<ChatColor> colors = new ArrayList<>();
if (gradient.size() < 2 || length < 2) {
if (gradient.isEmpty()) {
return gradient;
}
return Collections.singletonList(gradient.get(0));
}
float fPhase = 0;
float sectorLength = (float) (length - 1) / (gradient.size() - 1);
float factorStep = 1.0f / (sectorLength);
long index = 0;
int colorIndex = 0;
for (long i = 0; i < length; i++) {
if (factorStep * index > 1) {
colorIndex++;
index = 0;
}
float factor = factorStep * (index++ + fPhase);
// loop around if needed
if (factor > 1) {
factor = 1 - (factor - 1);
}
Color color = interpolate(
getColor(gradient.get(colorIndex), rgb),
getColor(gradient.get(Math.min(gradient.size() - 1, colorIndex + 1)), rgb),
factor
);
if (color != null) {
if (rgb) {
colors.add(ChatColor.of(color));
} else {
ChatColor chatColor = getClosestLegacy(color);
if (colors.isEmpty() || chatColor != colors.get(colors.size() - 1)) {
colors.add(chatColor);
}
}
}
}
return colors;
}
private static Color getColor(ChatColor color, boolean rgb) {
if (legacyColors.containsKey(color)) {
return legacyColors.get(color);
}
if (color.getName().startsWith("#")) {
Color c = new Color(Integer.parseInt(color.getName().substring(1), 16));
if (rgb) {
return c;
} else {
return legacyColors.get(getClosestLegacy(c));
}
} else if (rgb) {
return color.getColor();
}
return null;
}
private static Color interpolate(Color color1, Color color2, float factor) {
if (color1 == null || color2 == null) {
return null;
}
return new Color(
Math.round(color1.getRed() + factor * (color2.getRed() - color1.getRed())),
Math.round(color1.getGreen() + factor * (color2.getGreen() - color1.getGreen())),
Math.round(color1.getBlue() + factor * (color2.getBlue() - color1.getBlue()))
);
}
/**
* Check if a certain class exists. See {@link Class#forName(String)}
* @param className The class name to check
* @return <code>true</code> if the class exists, <code>false</code> if not
*/
public static boolean hasClass(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException classDoesntExist) {
return false;
}
}
/**
* Check if a class has a certain method. See {@link Class#getMethod(String, Class[])}
* @param clazz The class to check
* @param method The method to check for
* @param parameter Method parameter types
* @return <code>true</code> if the class has the method, <code>false</code> if not
*/
public static boolean hasMethod(Class<?> clazz, String method, Class<?>... parameter) {
try {
clazz.getMethod(method, parameter);
return true;
} catch (NoSuchMethodException methodDoesntExist) {
return false;
}
}
/**
* Get a method from a class if it exists. See {@link Class#getMethod(String, Class[])}
* @param clazz The class
* @param method The method name to get
* @param parameter Method parameter types
* @return the method, null if it doesn't exist
*/
public static Method getMethod(Class<?> clazz, String method, Class<?>... parameter) {
try {
return clazz.getMethod(method, parameter);
} catch (NoSuchMethodException methodDoesntExist) {
return null;
}
}
}

View File

@@ -0,0 +1,46 @@
package net.momirealms.customcrops.listener;
import dev.lone.itemsadder.api.CustomBlock;
import dev.lone.itemsadder.api.Events.CustomBlockBreakEvent;
import net.momirealms.customcrops.ConfigManager;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
public class BreakCustomBlock implements Listener {
@EventHandler
public void breakCustomBlock(CustomBlockBreakEvent event){
Player player =event.getPlayer();
Location location = event.getBlock().getLocation();
String namespacedId = event.getNamespacedID();
if(namespacedId.contains("stage")){
if(player.getInventory().getItemInMainHand().containsEnchantment(Enchantment.SILK_TOUCH) || player.getInventory().getItemInMainHand().getType() == Material.SHEARS){
event.setCancelled(true);
CustomBlock.place(namespacedId, location);
CustomBlock.byAlreadyPlaced(location.getBlock()).getLoot().forEach(itemStack -> {
location.getWorld().dropItem(location.clone().add(0.5,0.2,0.5), itemStack);
CustomBlock.remove(location);
});
}
}else if(namespacedId.equalsIgnoreCase(ConfigManager.Config.watered_pot) || namespacedId.equalsIgnoreCase(ConfigManager.Config.pot)){
World world = location.getWorld();
Block blockUp = location.add(0,1,0).getBlock();
if(CustomBlock.byAlreadyPlaced(blockUp) != null){
if(CustomBlock.byAlreadyPlaced(blockUp).getNamespacedID().contains("stage")){
for (ItemStack itemStack : CustomBlock.byAlreadyPlaced(blockUp).getLoot()) {
world.dropItem(location.clone().add(0.5, 0.2, 0.5), itemStack);
}
CustomBlock.remove(location);
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
package net.momirealms.customcrops.listener;
import dev.lone.itemsadder.api.CustomStack;
import net.momirealms.customcrops.ConfigManager;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntitySpawnEvent;
public class BreakFurniture implements Listener {
@EventHandler
public void breakFurniture(EntitySpawnEvent event){
Entity entity = event.getEntity();
if(!(entity instanceof Item)) return;
if(CustomStack.byItemStack(((Item) entity).getItemStack()) != null){
String namespacedId = CustomStack.byItemStack(((Item) entity).getItemStack()).getNamespacedID();
if(namespacedId.equalsIgnoreCase(ConfigManager.Config.sprinkler_1)){
entity.remove();
entity.getWorld().dropItem(entity.getLocation() ,CustomStack.getInstance(namespacedId + "_item").getItemStack());
}else if(namespacedId.equalsIgnoreCase(ConfigManager.Config.sprinkler_2)){
entity.remove();
entity.getWorld().dropItem(entity.getLocation() ,CustomStack.getInstance(namespacedId + "_item").getItemStack());
}else if(namespacedId.contains("_stage_")){
entity.remove();
}
}
}
}

View File

@@ -0,0 +1,99 @@
package net.momirealms.customcrops.listener;
import dev.lone.itemsadder.api.CustomStack;
import net.momirealms.customcrops.ConfigManager;
import net.momirealms.customcrops.datamanager.MaxSprinklersPerChunk;
import net.momirealms.customcrops.datamanager.SprinklerManager;
import net.momirealms.customcrops.IAFurniture;
import net.momirealms.customcrops.integrations.IntegrationCheck;
import net.momirealms.customcrops.MessageManager;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public class RightClickBlock implements Listener {
@EventHandler
public void rightClickBlock(PlayerInteractEvent event){
if(!event.hasItem()) return;
if(event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_AIR) return;
CustomStack customStack = CustomStack.byItemStack(event.getItem());
if(customStack != null){
Player player = event.getPlayer();
ItemStack itemStack = event.getItem();
String namespacedId = customStack.getNamespacedID();
//检测手中物品是否可能为水壶
if(itemStack.getType() == Material.WOODEN_SWORD){
List<Block> lineOfSight = player.getLineOfSight(null, 3);
//检测范围内是否有水
boolean hasWater = false;
for (final Block block : lineOfSight) {
if (block.getType() == Material.WATER) {
hasWater = true;
}
}
if(hasWater){
if(namespacedId.equalsIgnoreCase(ConfigManager.Config.watering_can_1) ||
namespacedId.equalsIgnoreCase(ConfigManager.Config.watering_can_2) ||
namespacedId.equalsIgnoreCase(ConfigManager.Config.watering_can_3)) {
if(customStack.getMaxDurability() == customStack.getDurability()){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.can_full,player);
}else {
customStack.setDurability(customStack.getDurability() + 1);
player.getWorld().playSound(player.getLocation(),Sound.ITEM_BUCKET_FILL,1,1);
}
}
}
return;
}
if(event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getBlockFace() == BlockFace.UP) {
if(namespacedId.equalsIgnoreCase(ConfigManager.Config.sprinkler_1i) || namespacedId.equalsIgnoreCase(ConfigManager.Config.sprinkler_2i)){
Location location = event.getClickedBlock().getLocation();
//兼容性检测
if(IntegrationCheck.PlaceCheck(location,player)){
return;
}
//高度限制
if(event.getClickedBlock().getY() > ConfigManager.Config.maxh || event.getClickedBlock().getY() < ConfigManager.Config.minh){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.bad_place,player);
return;
}
//此位置是否已有洒水器
if(MaxSprinklersPerChunk.alreadyPlaced(location)){
return;
}
//区块上限
if(MaxSprinklersPerChunk.maxSprinklersPerChunk(location)){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.limit_sprinkler.replace("{Max}", String.valueOf(ConfigManager.Config.max_sprinkler)),player);
return;
}
if(player.getGameMode() != GameMode.CREATIVE){
itemStack.setAmount(itemStack.getAmount() -1);
}
if(namespacedId.equalsIgnoreCase(ConfigManager.Config.sprinkler_1i)){
SprinklerManager.putInstance(location.clone().add(0,1,0),"s1");
}else {
SprinklerManager.putInstance(location.clone().add(0,1,0),"s2");
}
IAFurniture.placeFurniture(namespacedId.replace("_item",""),location.clone().add(0,1,0));
}
}
}
}
}

View File

@@ -0,0 +1,344 @@
package net.momirealms.customcrops.listener;
import dev.lone.itemsadder.api.CustomBlock;
import dev.lone.itemsadder.api.CustomStack;
import dev.lone.itemsadder.api.Events.CustomBlockInteractEvent;
import net.momirealms.customcrops.ConfigManager;
import net.momirealms.customcrops.CustomCrops;
import net.momirealms.customcrops.datamanager.CropManager;
import net.momirealms.customcrops.datamanager.MaxCropsPerChunk;
import net.momirealms.customcrops.integrations.IntegrationCheck;
import net.momirealms.customcrops.MessageManager;
import org.apache.commons.lang.StringUtils;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitScheduler;
import java.util.Objects;
public class RightClickCustomBlock implements Listener {
@EventHandler
public void rightClickCustomCrop(CustomBlockInteractEvent event){
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
//获取配置文件
Player player = event.getPlayer();
Block clickedBlock = event.getBlockClicked();
Location clickedBlockLocation = clickedBlock.getLocation();
/*
手里无物品,则进行收获判断
手里有物品,则进行浇水,骨粉,种植判断
*/
//手无物品部分
if (event.getItem() == null) {
CustomBlock clickedCustomBlock = CustomBlock.byAlreadyPlaced(clickedBlock);
if(clickedCustomBlock == null) return;
//兼容性检测
if(!IntegrationCheck.HarvestCheck(clickedBlockLocation, player)){
//获取点击方块的命名空间与ID
String namespacedID = clickedCustomBlock.getNamespacedID();
//如果ID内有stage则进行下一步
if (namespacedID.contains("stage")){
//是否为枯萎植物
if(namespacedID.equalsIgnoreCase(ConfigManager.Config.dead)) return;
//String namespace = clickedCustomBlock.getNamespacedID().split(":")[0];
String[] split = StringUtils.split(namespacedID,":");
//String[] cropNameList = clickedCustomBlock.getNamespacedID().split(":")[1].split("_");
String[] cropNameList = StringUtils.split(split[1],"_");
int nextStage = Integer.parseInt(cropNameList[2]) + 1;
//农作物是否存在下一阶段
if (CustomBlock.getInstance(split[0] + ":" + cropNameList[0] + "_stage_" + nextStage) == null) {
//如果不存在下一阶段说明已是最终阶段,可以收获
//遍历掉落物并删除方块
clickedCustomBlock.getLoot().forEach(itemStack -> clickedBlockLocation.getWorld().dropItem(clickedBlockLocation.clone().add(0.5,0.2,0.5),itemStack));
CustomBlock.remove(clickedBlockLocation);
//如果配置文件中有return项目则放置方块
FileConfiguration config = CustomCrops.instance.getConfig();
if(config.getConfigurationSection("crops." + cropNameList[0]).getKeys(false).contains("return")){
CustomBlock.place(config.getString("crops." + cropNameList[0] + ".return"), clickedBlockLocation);
}
}
}
}
}else{
//非空手状态
//右键的不是自定义方块返回
CustomBlock clickedCustomBlock = CustomBlock.byAlreadyPlaced(clickedBlock);
if(clickedCustomBlock == null) return;
//获取右键物品的namespaceID
String namespacedId = clickedCustomBlock.getNamespacedID();
/*
右键的是特殊作物吗
*/
if (namespacedId.contains("stage")) {
//下方方块不是自定义方块则返回
World world = player.getWorld();
Block blockUnder = clickedBlockLocation.clone().subtract(0,1,0).getBlock();
if (CustomBlock.byAlreadyPlaced(blockUnder) == null) return;
//检测右键的方块下方是否为干燥的种植盆方块
if (CustomBlock.byAlreadyPlaced(blockUnder).getNamespacedID().equalsIgnoreCase(ConfigManager.Config.pot)) {
//获取手中的物品
ItemStack mainHandItem = player.getInventory().getItemInMainHand();
Location locUnder = clickedBlockLocation.clone().subtract(0,1,0);
//如果手中的是水桶,那么转干为湿
if (mainHandItem.getType() == Material.WATER_BUCKET) {
//扣除水桶
if (player.getGameMode() != GameMode.CREATIVE) {
mainHandItem.setAmount(mainHandItem.getAmount() - 1);
player.getInventory().addItem(new ItemStack(Material.BUCKET));
}
CustomBlock.remove(locUnder);
CustomBlock.place(ConfigManager.Config.watered_pot, locUnder);
} else if (mainHandItem.getType() == Material.WOODEN_SWORD) {
waterPot(mainHandItem, player, locUnder);
}
}
//检测右键的方块下方是否为湿润的种植盆方块
else if(CustomBlock.byAlreadyPlaced(blockUnder).getNamespacedID().equalsIgnoreCase(ConfigManager.Config.watered_pot)){
//获取手中的物品
ItemStack mainHandItem = player.getInventory().getItemInMainHand();
//如果是骨粉
if (mainHandItem.getType() == Material.BONE_MEAL){
//植物是否具有stage属性
if (clickedCustomBlock.getNamespacedID().contains("stage")){
//获取点击方块的命名空间与ID
String namespacedID = clickedCustomBlock.getNamespacedID();
//String namespace = clickedCustomBlock.getNamespacedID().split(":")[0];
String[] split = StringUtils.split(namespacedID,":");
//String[] cropNameList = clickedCustomBlock.getNamespacedID().split(":")[1].split("_");
String[] cropNameList = StringUtils.split(split[1],"_");
//下一生长阶段
int nextStage = Integer.parseInt(cropNameList[2]) + 1;
//植物是否存在下一个stage
if (CustomBlock.getInstance(split[0]+ ":" + cropNameList[0] + "_stage_" + nextStage) != null){
if(player.getGameMode() != GameMode.CREATIVE){
mainHandItem.setAmount(mainHandItem.getAmount() - 1);
}
//骨粉的成功率
if (Math.random() < ConfigManager.Config.bone_chance){
CustomBlock.remove(clickedBlockLocation);
CustomBlock.place(split[0] + ":" + cropNameList[0] + "_stage_" + nextStage,clickedBlockLocation);
Particle particleSuccess = Particle.valueOf(ConfigManager.Config.success);
world.spawnParticle(particleSuccess, clickedBlockLocation.clone().add(0.5, 0.1,0.5), 1 ,0,0,0,0);
//使用骨粉是否消耗水分
if(ConfigManager.Config.need_water){
CustomBlock.remove(clickedBlockLocation.clone().subtract(0,1,0));
CustomBlock.place(ConfigManager.Config.pot, clickedBlockLocation.clone().subtract(0,1,0));
}
}else {
Particle particleFailure = Particle.valueOf(ConfigManager.Config.failure);
world.spawnParticle(particleFailure, clickedBlockLocation.clone().add(0.5, 0.1,0.5), 1 ,0,0,0,0);
}
}
}
}
}
}
/*
右键的是种植盆吗
*/
else if (event.getBlockFace() == BlockFace.UP){
//获取手中的物品
ItemStack mainHandItem = player.getInventory().getItemInMainHand();
//检测右键的方块是否为干燥的种植盆方块
if (namespacedId.equalsIgnoreCase(ConfigManager.Config.pot)){
//如果手中的是水桶,那么转干为湿
if (mainHandItem.getType() == Material.WATER_BUCKET){
//扣除水桶
if(player.getGameMode() != GameMode.CREATIVE){
mainHandItem.setAmount(mainHandItem.getAmount() - 1);
player.getInventory().addItem(new ItemStack(Material.BUCKET));
}
CustomBlock.remove(clickedBlockLocation);
CustomBlock.place(ConfigManager.Config.watered_pot,clickedBlockLocation);
} else if (mainHandItem.getType() == Material.WOODEN_SWORD){
waterPot(mainHandItem, player,clickedBlockLocation);
} else {
tryPlantSeed(clickedBlockLocation, mainHandItem, player);
}
}
//检测右键的方块是否为湿润的种植盆方块
else if(namespacedId.equalsIgnoreCase(ConfigManager.Config.watered_pot)){
tryPlantSeed(clickedBlockLocation, mainHandItem, player);
}
}
}
}
//尝试种植植物
private void tryPlantSeed(Location clickedBlockLocation, ItemStack mainHandItem, Player player) {
//是否为IA物品
if(CustomStack.byItemStack(mainHandItem) == null) return;
//获取命名空间名与ID
String namespaced_id = CustomStack.byItemStack(mainHandItem).getNamespacedID();
//是否为种子
if (namespaced_id.endsWith("_seeds")){
//获取农作物名
String cropName = StringUtils.split(namespaced_id.replace("_seeds",""),":")[1];
//String[] crop = CustomStack.byItemStack(mainHandItem).getNamespacedID().toLowerCase().replace("_seeds","").split(":");
//检测上方为空气
if(clickedBlockLocation.clone().add(0, 1, 0).getBlock().getType() != Material.AIR){
return;
}
FileConfiguration config = CustomCrops.instance.getConfig();
//该种子是否存在于配置文件中
if(!config.getConfigurationSection("crops").getKeys(false).contains(cropName)){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.no_such_seed,player);
return;
}
//是否超高超低
if (clickedBlockLocation.getY() < ConfigManager.Config.minh || clickedBlockLocation.getY() > ConfigManager.Config.maxh){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.bad_place,player);
return;
}
Location locUp = clickedBlockLocation.clone().add(0,1,0);
//是否启用了季节
Label_out:
if(ConfigManager.Config.season){
if(ConfigManager.Config.greenhouse){
int range = ConfigManager.Config.range;
World world = player.getWorld();
for(int i = 1; i <= range; i++){
Location tempLocation = locUp.clone().add(0,i,0);
if (CustomBlock.byAlreadyPlaced(tempLocation.getBlock()) != null){
if(CustomBlock.byAlreadyPlaced(tempLocation.getBlock()).getNamespacedID().equalsIgnoreCase(ConfigManager.Config.glass)){
break Label_out;
}
}
}
}
//获取种子适宜生长的季节
String[] seasons = config.getString("crops."+cropName+".season").split(",");
boolean wrongSeason = true;
for(String season : seasons){
if (Objects.equals(season, ConfigManager.Config.current)) {
wrongSeason = false;
break;
}
}
if(wrongSeason){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.wrong_season,player);
return;
}
}
//是否到达区块上限
if(MaxCropsPerChunk.maxCropsPerChunk(clickedBlockLocation)){
MessageManager.playerMessage(ConfigManager.Config.prefix + ConfigManager.Config.limit_crop.replace("{Max}", String.valueOf(ConfigManager.Config.max_crop)),player);
return;
}
//添加到缓存中
if(ConfigManager.Config.season){
CropManager.putInstance(locUp, config.getString("crops."+cropName+".season"));
}else{
CropManager.putInstance(locUp, "all");
}
//减少种子数量
if(player.getGameMode() != GameMode.CREATIVE){
mainHandItem.setAmount(mainHandItem.getAmount() -1);
}
//放置自定义农作物
CustomBlock.place(namespaced_id.replace("_seeds","_stage_1"),locUp);
}
}
private void waterPot(ItemStack itemStack, Player player, Location location){
//是否为IA物品
if(CustomStack.byItemStack(itemStack) == null) return;
BukkitScheduler bukkitScheduler = Bukkit.getScheduler();
bukkitScheduler.runTaskAsynchronously(CustomCrops.instance,()-> {
//获取IA物品
CustomStack customStack = CustomStack.byItemStack(itemStack);
String namespacedId = customStack.getNamespacedID();
World world = player.getWorld();
int x;
int z;
if (namespacedId.equalsIgnoreCase(ConfigManager.Config.watering_can_1)) {
x = 0;
z = 0;
} else if (namespacedId.equalsIgnoreCase(ConfigManager.Config.watering_can_2)){
x = 2;
z = 2;
} else if (namespacedId.equalsIgnoreCase(ConfigManager.Config.watering_can_3)){
x = 4;
z = 4;
} else return;
//虽Who cares一点水呢
if(customStack.getDurability() > 0){
CustomStack.byItemStack(itemStack).setDurability(CustomStack.byItemStack(itemStack).getDurability() - 1);
}else return;
//播放洒水音效
world.playSound(player.getLocation(),Sound.BLOCK_WATER_AMBIENT,1,1);
//获取玩家朝向
float yaw = player.getLocation().getYaw();
//提前获取ID与命名空间以免for循环循环get
String wateredPot = ConfigManager.Config.watered_pot;
String pot = ConfigManager.Config.pot;
//根据朝向确定浇水方向
if (yaw <= 45 && yaw >= -135) {
if (yaw > -45) {
x = 0;
} else {
z = 0;
}
for (int i = 0; i <= x; i++) {
for (int j = 0; j <= z; j++) {
Location tempLoc = location.clone().add(i, 0, j);
if(CustomBlock.byAlreadyPlaced(tempLoc.getBlock()) != null){
if(CustomBlock.byAlreadyPlaced(tempLoc.getBlock()).getNamespacedID().equalsIgnoreCase(pot)){
//同步替换方块
bukkitScheduler.callSyncMethod(CustomCrops.instance,()->{
CustomBlock.remove(tempLoc);
CustomBlock.place(wateredPot,tempLoc);
return null;
});
}
}
}
}
} else {
if (yaw < 135 && yaw > 45) {
z= 0;
} else {
x= 0;
}
for (int i = 0; i <= x; i++) {
for (int j = 0; j <= z; j++) {
Location tempLoc = location.clone().subtract(i, 0, j);
if(CustomBlock.byAlreadyPlaced(tempLoc.getBlock()) != null){
if(CustomBlock.byAlreadyPlaced(tempLoc.getBlock()).getNamespacedID().equalsIgnoreCase(pot)){
//同步替换方块
bukkitScheduler.callSyncMethod(CustomCrops.instance,()->{
CustomBlock.remove(tempLoc);
CustomBlock.place(wateredPot,tempLoc);
return null;
});
}
}
}
}
}
});
}
}