刷新实体插件,除了当玩家跑出一定距离后despawn刷新的实体个人认为有争议外,其他基本都完成了,可以通过despawn控制是否刷新实体, 缩写为dsp,并且dsp on/off 玩家姓名 可以控制是否在玩家附近刷新实体,如果单输入despawn或者dsp的话,会关闭所有玩家的实体刷新

This commit is contained in:
yuyu 2024-04-04 21:28:26 +08:00
parent 83f4e19445
commit b35e0055f5
12 changed files with 420 additions and 20 deletions

10
.idea/libraries/lib.xml generated Normal file
View File

@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="lib">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
<jarDirectory url="file://$PROJECT_DIR$/lib" recursive="false" />
</library>
</component>

View File

@ -19,6 +19,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="lib" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />

View File

@ -1,5 +1,9 @@
package com.yuyu.srwildentity;
import com.yuyu.srwildentity.config.ConfigManager;
import com.yuyu.srwildentity.listener.EntityRefreshListener;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.plugin.java.JavaPlugin;
/**
@ -13,12 +17,31 @@ import org.bukkit.plugin.java.JavaPlugin;
public final class SrWildEntity extends JavaPlugin {
private ConfigManager configManager;
@Override
public void onEnable() {
// Plugin startup logic
getLogger().info(ChatColor.AQUA+"SrWildEntity开始运行");
onload();
//TODO(注册监听类,测试)
EntityRefreshListener entityRefreshListener = new EntityRefreshListener(this.getLogger(),configManager,this);
Bukkit.getPluginManager().registerEvents(entityRefreshListener,this);
this.getCommand("despawn").setExecutor(entityRefreshListener);
}
/**
*
*/
public void onload(){
this.configManager = new ConfigManager(this);
}
@Override
public void onDisable() {
// Plugin shutdown logic

View File

@ -20,6 +20,7 @@ public class ConfigManager {
private final BiomeEntityRefreshSettings biomeEntityRefreshSettings;//读取了群系相关刷新条件和需要刷新方块
private final int refreshTime;
private final int total;
private final int num;
public ConfigManager(Plugin plugin) {
//加载配置文件
@ -32,6 +33,7 @@ public class ConfigManager {
this.refreshTime = config.getInt("RefreshTime");
this.total = config.getInt("total");
this.num = config.getInt("num");
}
public BiomeEntityRefreshSettings getBiomeEntityRefreshSettings() {
@ -42,6 +44,10 @@ public class ConfigManager {
return refreshTime;
}
public int getNum() {
return num;
}
public int getTotal() {
return total;
}

View File

@ -1,5 +1,6 @@
package com.yuyu.srwildentity.config.condition;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
@ -18,18 +19,18 @@ import java.util.List;
* @Description: 读取实体刷新的配置相关文件
*/
public class BiomeEntityRefreshSettings {
private final HashMap<String,List<String>> biomeEntityMap;
private final HashMap<String,List<String>> biomeEntityMap;//此集合储存了群系内需要刷新的怪物
private final HashMap<String,HashMap<String,EntityCondition>> biomeEntityConditionMap;
private final HashMap<String,HashMap<String,EntityCondition>> biomeEntityConditionMap;//此集合储存需要刷新的怪物的相关配置
public BiomeEntityRefreshSettings(Plugin plugin) {
this.biomeEntityMap = new HashMap<>();
this.biomeEntityConditionMap = new HashMap<>();
loadBiomeEntitiesConfig(plugin);
this.loadBiomeEntitiesConfig(plugin);
loadEntityConditionConfig(plugin);
this.loadEntityConditionConfig(plugin);
}
@ -41,17 +42,21 @@ public class BiomeEntityRefreshSettings {
File file = new File(plugin.getDataFolder(), "entityCondition.yml");
config = YamlConfiguration.loadConfiguration(file);
for (String biomeName : this.biomeEntityMap.keySet()){
plugin.getLogger().info(ChatColor.GOLD+biomeName);
HashMap<String,EntityCondition> entityConditionHashMap = new HashMap<>();
List<String> entities = this.biomeEntityMap.get(biomeName);
for (String entityName : entities){
EntitySite entitySite = EntitySite.fromId(config.getInt(biomeName + "." + entityName + ".site"));
int light = config.getInt(biomeName+"."+entityName+".light");
long time = config.getLong(biomeName+"."+entityName+".timing");
long stime = config.getLong(biomeName+"."+entityName+".startTiming");
long etime = config.getLong(biomeName+"."+entityName+".endTiming");
int nums = config.getInt(biomeName+"."+entityName+".nums");
int yMax = config.getInt(biomeName+"."+entityName+".yMax");
int yMin = config.getInt(biomeName+"."+entityName+".yMin");
EntityCondition entityCondition = new EntityCondition(entityName, biomeName, entitySite, light, time, nums, yMax, yMin);
EntityCondition entityCondition = new EntityCondition(entityName, biomeName, entitySite, light, stime,etime, nums, yMax, yMin);
plugin.getLogger().info(ChatColor.AQUA+entityCondition.toString());
//存入单个群系刷新map
entityConditionHashMap.put(entityName, entityCondition);
@ -78,6 +83,8 @@ public class BiomeEntityRefreshSettings {
biomeconfig = YamlConfiguration.loadConfiguration(biomeName);
List<String> biomeList = biomeconfig.getStringList("biome");
if (config == null || biomeconfig == null){
plugin.getLogger().info("biomeEntity.yml或者 biome.yml文件读取失败!");
return;
@ -89,13 +96,19 @@ public class BiomeEntityRefreshSettings {
if (config.contains(section)){
List<String> entityList = config.getStringList(section+".ENTITY");
this.biomeEntityMap.put(section,entityList);
plugin.getLogger().info(ChatColor.MAGIC+section+ entityList.toString());
}
}
plugin.getLogger().info("biomeEntity.读取完成");
}
public HashMap<String, List<String>> getEntityMap() {
public HashMap<String, List<String>> getBiomeEntityMap() {
return biomeEntityMap;
}
public HashMap<String, HashMap<String, EntityCondition>> getBiomeEntityConditionMap() {
return biomeEntityConditionMap;
}
}

View File

@ -14,17 +14,19 @@ public class EntityCondition {
private final String biome;//实体刷新的群系
private final EntitySite entitySite;//刷新位置
private final int light;//刷新亮度
private final long timing;//刷新时间
private final long startTiming;//刷新时间
private final long endTimeing;//结束刷新的时间
private final int nums;//刷新的数量
private final int yMax;
private final int yMin;
public EntityCondition(String entityName, String biome, EntitySite entitySite, int light, long timing, int nums, int yMax, int yMin) {
public EntityCondition(String entityName, String biome, EntitySite entitySite, int light, long startTiming,long endTiming, int nums, int yMax, int yMin) {
this.entityName = entityName;
this.biome = biome;
this.entitySite = entitySite;
this.light = light;
this.timing = timing;
this.startTiming = startTiming;
this.endTimeing = endTiming;
this.nums = nums;
this.yMax = yMax;
this.yMin = yMin;
@ -46,8 +48,12 @@ public class EntityCondition {
return light;
}
public long getTiming() {
return timing;
public long getStartTiming() {
return startTiming;
}
public long getEndTimeing() {
return endTimeing;
}
public int getNums() {
@ -61,4 +67,19 @@ public class EntityCondition {
public int getyMin() {
return yMin;
}
@Override
public String toString() {
return "EntityCondition{" +
"entityName='" + entityName + '\'' +
", biome='" + biome + '\'' +
", entitySite=" + entitySite +
", light=" + light +
", startTiming=" + startTiming +
", endTimeing=" + endTimeing +
", nums=" + nums +
", yMax=" + yMax +
", yMin=" + yMin +
'}';
}
}

View File

@ -4,6 +4,7 @@ package com.yuyu.srwildentity.config.condition;
* 实体刷新的位置
*/
public enum EntitySite {
NULL(0),//
ON_GROUND(1), //在地上
ON_WATER(2), //在水上

View File

@ -1,9 +1,34 @@
package com.yuyu.srwildentity.listener;
import com.palmergames.bukkit.towny.TownyAPI;
import com.palmergames.bukkit.towny.event.PlayerEnterTownEvent;
import com.palmergames.bukkit.towny.event.PlayerLeaveTownEvent;
import com.palmergames.bukkit.towny.object.Town;
import com.yuyu.srwildentity.conditionCheck.ConditionCheck;
import com.yuyu.srwildentity.config.ConfigManager;
import com.yuyu.srwildentity.config.condition.EntityCondition;
import com.yuyu.srwildentity.config.condition.EntitySite;
import com.yuyu.srwildentity.pojo.PlayerRefreshinfo;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.logging.Logger;
/**
* @BelongsProject: SrWildEntity
@ -15,8 +40,278 @@ import org.bukkit.event.Listener;
* @Description:监听类控制怪物的刷新,同时注册指令操控刷新
*/
public class EntityRefreshListener implements Listener, CommandExecutor {
private ConfigManager configManager;//配置相关
private boolean flag; //用于验证entity是否刷新
private Logger logger;//日志打印
private Random random;//生成随机数,随机实体的定位
private HashMap<UUID,String> entityUUIDToPlayer;
private HashMap<String, PlayerRefreshinfo> refreshPlayer;
private List<String> noRefreshPlayer;//记录不需要刷新的玩家
private Plugin plugin;
public EntityRefreshListener(Logger logger, ConfigManager configManager, Plugin plugin){
this.entityUUIDToPlayer = new HashMap<>();
this.refreshPlayer = new HashMap<>();
this.configManager = configManager;
this.random = new Random();
this.logger = logger;
this.flag = true;
this.noRefreshPlayer = new ArrayList<>();
this.plugin = plugin;
logger.info(ChatColor.DARK_GREEN+"SrWildEntity定时任务触发");
//注册定时任务
plugin.getServer().getScheduler().scheduleSyncRepeatingTask
(this.plugin,this::timedRdfreshEneity,0,configManager.getRefreshTime()*20);
}
/**
* 此类用于获取玩家的危险度
* @param player
*/
public int prRisk(Player player){
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
String placeholder = PlaceholderAPI.setPlaceholders(player, "%prisk_level%");
int level = Integer.parseInt(placeholder);
return level;
}else {
return 0;
}
}
/**
*注册的指令可以通过指令去调解flag
*/
@Override
public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) {
if (!s.equalsIgnoreCase("despawn") && !s.equalsIgnoreCase("dsp")){
return false;
}
if (commandSender.isOp()){
if (strings.length == 0){
//只输入despawn,直接停止刷新
flag = !flag;
return true;
}else {
String choice = strings[0];//只能是on或者off
if (choice.equalsIgnoreCase("on") || choice.equalsIgnoreCase("off")){
if (choice.equalsIgnoreCase("on")){
Player player = plugin.getServer().getPlayer(strings[1]);
if (player == null){
commandSender.sendMessage("请输入正确的玩家姓名!");
return true;
}else {
noRefreshPlayer.remove(strings[1]);
PlayerRefreshinfo playerRefreshinfo = new PlayerRefreshinfo(strings[1], 0, new ArrayList<>());
refreshPlayer.put(strings[1], playerRefreshinfo);
commandSender.sendMessage(ChatColor.YELLOW+strings[1]+"开始刷怪");
return true;
}
}else {
Player player = plugin.getServer().getPlayer(strings[1]);
if (player == null){
commandSender.sendMessage("请输入正确的玩家姓名!");
return true;
}else {
//玩家不为空
refreshPlayer.remove(strings[1]);
noRefreshPlayer.add(strings[1]);
return true;
}
}
}
}
}else {
commandSender.sendMessage(ChatColor.RED+"只有op能执行");
return false;
}
return false;
}
/**
* 玩家进入城镇,
* @param event
*/
@EventHandler
public void onPlayerInTown(PlayerEnterTownEvent event) {
if (flag){
String name = event.getPlayer().getName();
refreshPlayer.remove(name);
logger.info(name + "开始停止怪物");
}
}
/**
* 玩家离开城镇
* @param event
*/
@EventHandler
public void onPlayerLeaveTown(PlayerLeaveTownEvent event) {
if (flag) {
String name = event.getPlayer().getName();
if (!noRefreshPlayer.contains(name)) {
PlayerRefreshinfo playerRefreshinfo = new PlayerRefreshinfo(name, 0, new ArrayList<>());
refreshPlayer.put(name, playerRefreshinfo);
logger.info(name + "开始刷新实体");
}
}
}
/**
* 玩家进入游戏后要判断玩家登录的位置是否需要刷新怪物
* @param event
*/
@EventHandler
public void onPlayerJoinGame(PlayerJoinEvent event) {
if (flag){
Player player = event.getPlayer();
String name = player.getName();
Town town = TownyAPI.getInstance().getTown(player.getLocation());
if (town == null && !noRefreshPlayer.contains(name)) {
PlayerRefreshinfo playerRefreshinfo = new PlayerRefreshinfo(name, 0, new ArrayList<>());
refreshPlayer.put(name, playerRefreshinfo);
logger.info(ChatColor.MAGIC + player.getName() + "在野外登录,开始刷新实体");
}
}
}
/**
* 用于玩家下线后停止刷新移出list集合
* @param event
*/
@EventHandler
public void onPlayerOffOnline(PlayerQuitEvent event){
if (flag) {
Player player = event.getPlayer();
String name = player.getName();
//下线后移出刷新列表
if (refreshPlayer.get(name) != null) {
refreshPlayer.remove(name);
logger.info(ChatColor.GOLD + "玩家被移出刷新集合");
}
}
}
/**
* 相关怪物死亡后验证是否本插件刷新再考虑需不需要操作
* @param event
*/
@EventHandler
public void onEntityDeath(EntityDeathEvent event) {
if (flag){
LivingEntity entity = event.getEntity();
UUID uniqueId = entity.getUniqueId();
if (entityUUIDToPlayer.containsKey(uniqueId)) {
//通过死亡实体的uuid获取玩家姓名然后删除uuid
String playerName = entityUUIDToPlayer.get(entityUUIDToPlayer);
refreshPlayer.get(playerName).delEntityList(uniqueId);
}
}
}
/**
* 定时任务每隔一段时间在玩家附近刷新实体
*/
public void timedRdfreshEneity(){
if (flag) {
int num = configManager.getNum();//每次执行时刷新的总数
for (String name : refreshPlayer.keySet()) {
int sum = 0;//用于记录此次刷新生成的总数
//获取玩家信息
Player player = plugin.getServer().getPlayer(name);
PlayerRefreshinfo playerRefreshinfo = refreshPlayer.get(name);
//玩家危险度,用于刷新实体的等级
int level = this.prRisk(player);
//TODO(危险度提示,后续需要使用这个变量去控制怪物的参数等级`)
logger.info(ChatColor.GREEN + "危险度为:" + level);
Location playerlocation = player.getLocation();
//获取玩家位置信息
World world = playerlocation.getWorld();
int blockZ = playerlocation.getBlockZ();
int blockX = playerlocation.getBlockX();
Biome biome = world.getBiome(blockX, blockZ);
String biomeName = biome.name();
//需要刷新的entity列表
List<String> entityList = configManager.getBiomeEntityRefreshSettings().getBiomeEntityMap().get(biomeName);
//entity条件配置
HashMap<String, EntityCondition> entityConditionHashMap =
configManager.getBiomeEntityRefreshSettings().getBiomeEntityConditionMap().get(biomeName);
if (entityList == null || entityConditionHashMap == null){
throw new RuntimeException("entityList和entityConditionHashMap读取出错");
}
for (String entityName : entityList){
//获取entity配置
EntityCondition entityCondition = entityConditionHashMap.get(entityName);
int nums = entityCondition.getNums();//当前实体需要刷新的数量
for (int i = 0;i<nums &&
playerRefreshinfo.getEntityList().size() < configManager.getTotal()
&& sum <= num; i++) {
// 生成x和y坐标范围在-30到30之间包括-30和30
int x = random.nextInt(61) - 30; // 生成0到60之间的随机数然后减去30得到-30到30的范围
int z = random.nextInt(61) - 30; // 同上,在玩家附近随机位置生成
int y;
//刷新在地上直接获取最高的x z 最高处的坐标
if (entityCondition.getEntitySite() == EntitySite.ON_GROUND) {
y = world.getHighestBlockYAt(x, z);
} else {
int range = entityCondition.getyMax() - entityCondition.getyMin() + 1;
y = random.nextInt(range) - 1;//随机高度
}
Location location = new Location(world, x, y + 1, z);
//判断位置是否符合
if (ConditionCheck.checkEntityRefresh(world, location, entityCondition)) {
//通过则生成
Entity entity = world.spawnEntity(location, EntityType.valueOf(entityCondition.getEntityName()));
logger.info(ChatColor.GOLD+""+x+" "+y+" "+z+"位置刷新了"+entityName);
UUID uniqueId = entity.getUniqueId();
//存入玩家对应的刷新的实体集合便于寻找
entityUUIDToPlayer.put(uniqueId,name);
//存入
playerRefreshinfo.addEntityList(uniqueId);
sum++;
}
}
}
}
}
}
public ConfigManager getConfigManager() {
return configManager;
}
public List<String> getNoRefreshPlayer() {
return noRefreshPlayer;
}
}

View File

@ -1,5 +1,6 @@
DESERT: #群系名称,下方标出该群系需要刷新的entity
ENTITY: #该群系刷新的entity,具体的怪物刷新配置需要去entityConfig.yml中配置,!!注意此处的实体配置一定要在实体配置文件中存在,否则会不会刷新!
- ZOMBIE
- SKULL
ENTITY: [ZOMBIE,PIG_ZOMBIE] #该群系刷新的entity,具体的怪物刷新配置需要去entityConfig.yml中配置,!!注意此处的实体配置一定要在实体配置文件中存在,否则会不会刷新!
ROOFED_FOREST: #群系名称,下方标出该群系需要刷新的entity
ENTITY: [ZOMBIE,PIG_ZOMBIE] #该群系刷新的entity,具体的怪物刷新配置需要去entityConfig.yml中配置,!!注意此处的实体配置一定要在实体配置文件中存在,否则会不会刷新!

View File

@ -1,2 +1,3 @@
RefreshTime: 100 #刷新时间单位为s
total: 10 #每个玩家身边刷新的总数
RefreshTime: 10 #刷新时间单位为s
total: 12 #每个玩家每次刷新的实体数量
num: 4 #单次刷新的数量每RefreshTime秒刷新的数量

View File

@ -1,8 +1,27 @@
DESERT:
ZOMBIE: #指出entity在该群系的刷新条件
site: 1 #刷新位置,数字代表不同的刷新位置
light: 100 #刷新亮度
timing: 4086 #刷新怪物的时间注意这里需要用mc中的时间格式来表示而不是现实中的时间
light: 15 #刷新亮度
startTiming: 0 #刷新怪物的时间注意这里需要用mc中的时间格式来表示而不是现实中的时间
endTiming: 20000 #结束刷新的时间,怪物会在这个区间内刷新
nums: 2 #entity的刷新数量注意这里的数量会和通群系中其他entity相关总数不会超过config.yml中的定义
yMax: 150 #极限刷新高度
yMin: 60 #最小刷新高度
ROOFED_FOREST:
ZOMBIE: #指出entity在该群系的刷新条件
site: 1 #刷新位置,数字代表不同的刷新位置
light: 15 #刷新亮度
startTiming: 0 #刷新怪物的时间注意这里需要用mc中的时间格式来表示而不是现实中的时间
endTiming: 24000 #结束刷新的时间,怪物会在这个区间内刷新
nums: 2 #entity的刷新数量注意这里的数量会和通群系中其他entity相关总数不会超过config.yml中的定义
yMax: 150 #极限刷新高度
yMin: 60 #最小刷新高度
PIG_ZOMBIE: #指出entity在该群系的刷新条件
site: 1 #刷新位置,数字代表不同的刷新位置
light: 15 #刷新亮度
startTiming: 0 #刷新怪物的时间注意这里需要用mc中的时间格式来表示而不是现实中的时间
endTiming: 24000 #结束刷新的时间,怪物会在这个区间内刷新
nums: 2 #entity的刷新数量注意这里的数量会和通群系中其他entity相关总数不会超过config.yml中的定义
yMax: 150 #极限刷新高度
yMin: 60 #最小刷新高度

View File

@ -1,3 +1,12 @@
name: SrWildEntity
version: '${project.version}'
main: com.yuyu.srwildentity.SrWildEntity
description: 实体刷新插件
authors: [Yuyu]
commands:
despawn:
usage: /<command>
aliases: [dsp]
description: 只有op能使用的插件
permission: op
permission-message: 你没有使用权限!