实现了通过boss条显示进度

实现了通过title告警和提示
实现了多玩家分队列并行提交(但是依然顺序执行)
实现了快速跳过不可操作方块
This commit is contained in:
张宇衡 2023-06-02 16:08:41 +08:00
parent 1fb5691b32
commit a9fd205c0c
12 changed files with 383 additions and 72 deletions

View File

@ -3,7 +3,6 @@ package site.deercloud.liteworldedit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@ -12,8 +11,6 @@ import org.bukkit.inventory.ItemStack;
import site.deercloud.liteworldedit.JobGenerator.Empty;
import site.deercloud.liteworldedit.JobGenerator.Fill;
import site.deercloud.liteworldedit.Jobs.Job;
import site.deercloud.liteworldedit.Jobs.Place;
import site.deercloud.liteworldedit.Jobs.Remove;
import site.deercloud.liteworldedit.Managers.Point;
import java.util.*;
@ -41,7 +38,7 @@ public class Commands implements TabExecutor {
return true;
}
Point point = new Point(x, y, z, player);
LiteWorldEdit.instance.getCache().add_point(player, index, point);
LiteWorldEdit.instance.getCache().addPoint(player, index, point);
sender.sendMessage("" + index + " 已设置为 " + x + ", " + y + ", " + z + "");
} catch (NumberFormatException e) {
sender.sendMessage("参数错误。");
@ -55,7 +52,7 @@ public class Commands implements TabExecutor {
} else if (Objects.equals(args[0], "points")) {
if (sender instanceof Player) {
Player player = (Player) sender;
Map<Integer, Point> points = LiteWorldEdit.instance.getCache().get_points(player);
Map<Integer, Point> points = LiteWorldEdit.instance.getCache().getPoints(player);
if (points != null) {
sender.sendMessage("你创建的点:");
for (Map.Entry<Integer, Point> entry : points.entrySet()) {
@ -68,14 +65,14 @@ public class Commands implements TabExecutor {
} else {
sender.sendMessage("该命令只能由玩家执行。");
}
} else if (Objects.equals(args[0], "place")) {
} else if (Objects.equals(args[0], "fill")) {
if (sender instanceof Player) {
Player player = (Player) sender;
if (args.length == 3) {
try {
Integer indexA = Integer.parseInt(args[1]);
Integer indexB = Integer.parseInt(args[2]);
Map<Integer, Point> points = LiteWorldEdit.instance.getCache().get_points(player);
Map<Integer, Point> points = LiteWorldEdit.instance.getCache().getPoints(player);
if (points == null) {
sender.sendMessage("你没有设置任何点。");
return true;
@ -108,14 +105,14 @@ public class Commands implements TabExecutor {
} else {
sender.sendMessage("该命令只能由玩家执行。");
}
} else if (Objects.equals(args[0], "break")) {
} else if (Objects.equals(args[0], "empty")) {
if (sender instanceof Player) {
Player player = (Player) sender;
if (args.length == 3) {
try {
Integer indexA = Integer.parseInt(args[1]);
Integer indexB = Integer.parseInt(args[2]);
Map<Integer, Point> points = LiteWorldEdit.instance.getCache().get_points(player);
Map<Integer, Point> points = LiteWorldEdit.instance.getCache().getPoints(player);
if (points == null) {
sender.sendMessage("你没有设置任何点。");
return true;
@ -152,14 +149,14 @@ public class Commands implements TabExecutor {
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (args.length == 1) {
return Arrays.asList("point", "p", "points", "place", "break", "help");
return Arrays.asList("point", "p", "points", "fill", "empty", "help");
} else if (args.length == 2) {
switch (args[1]) {
case "point":
return Collections.singletonList("[点序号(整数)] [x] [y] [z]");
case "place":
case "fill":
return Collections.singletonList("[点序号A] [点序号B] (需要手持被放置的方块)");
case "break":
case "empty":
return Collections.singletonList("[点序号A] [点序号B] (需要拥有下届合金锄)");
}
}
@ -168,10 +165,10 @@ public class Commands implements TabExecutor {
public void print_help(CommandSender sender) {
sender.sendMessage(ChatColor.GREEN + "LiteWorldEdit 帮助");
sender.sendMessage(ChatColor.GREEN + "/lwe point [点序号(整数)] [x] [y] [z] - 创建点");
sender.sendMessage(ChatColor.GREEN + "/lwe point|p [点序号(整数)] [x] [y] [z] - 创建点");
sender.sendMessage(ChatColor.GREEN + "/lwe points - 查看所有点");
sender.sendMessage(ChatColor.GREEN + "/lwe place [点序号A] [点序号B] - (在AB点对角线间放置方块 - 需要手持被放置的方块)");
sender.sendMessage(ChatColor.GREEN + "/lwe break [点序号A] [点序号B] - (破坏AB点对角线间方块 - 需要拥有下届合金镐)");
sender.sendMessage(ChatColor.GREEN + "/lwe fill [点序号A] [点序号B] - (在AB点对角线间放置方块 - 需要手持被放置的方块)");
sender.sendMessage(ChatColor.GREEN + "/lwe empty [点序号A] [点序号B] - (破坏AB点对角线间方块 - 需要拥有下届合金镐)");
sender.sendMessage(ChatColor.GREEN + "/lwe help - 查看帮助");
}

View File

@ -2,17 +2,13 @@ package site.deercloud.liteworldedit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.EventListener;
public class Events implements Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
LiteWorldEdit.instance.getCache().delete_player_points(event.getPlayer());
LiteWorldEdit.instance.getCache().delete_player_jobs(event.getPlayer());
LiteWorldEdit.instance.getCache().deletePlayerCache(event.getPlayer());
}
}

View File

@ -2,7 +2,6 @@ package site.deercloud.liteworldedit.JobGenerator;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import site.deercloud.liteworldedit.Jobs.Remove;
import site.deercloud.liteworldedit.LiteWorldEdit;
@ -16,7 +15,7 @@ public class Empty {
for (int z = Math.min(p1.z, p2.z); z <= Math.max(p1.z, p2.z); z++) {
Location location = new Location(world, (double) x, (double) y, (double) z);
Remove remove_job = new Remove(location, player);
LiteWorldEdit.instance.getCache().add_job(player, remove_job);
LiteWorldEdit.instance.getCache().addJob(player, remove_job);
}
}
}

View File

@ -4,7 +4,6 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import site.deercloud.liteworldedit.Jobs.Place;
import site.deercloud.liteworldedit.LiteWorldEdit;
@ -18,7 +17,7 @@ public class Fill {
for (int z = Math.min(p1.z, p2.z); z <= Math.max(p1.z, p2.z); z++) {
Location location = new Location(world, (double) x, (double) y, (double) z);
Place place_job = new Place(location, player, block);
LiteWorldEdit.instance.getCache().add_job(player, place_job);
LiteWorldEdit.instance.getCache().addJob(player, place_job);
}
}
}

View File

@ -6,17 +6,6 @@ import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
enum JobErrCode {
OK, // 无错误
NO_PERMISSION, // 没有权限
NO_PICKAXE, // 没有下界合金镐
NOT_ENOUGH_DURATION, // 耐久不足
NOT_ENOUGH_ITEMS, // 物品不足
OUT_OF_RANGE, // 超出距离
NOT_AIR_BLOCK, // 不是空气方块
NO_BREAKABLE, // 不可破坏
}
public class Job {
protected World _world;
protected Location _location;

View File

@ -0,0 +1,37 @@
package site.deercloud.liteworldedit.Jobs;
public enum JobErrCode {
OK(0, ""), // 无错误
// ------------ 以下结果应当直接取消任务 ------------
NO_PICKAXE(101, "没有下界合金镐"),
NOT_ENOUGH_DURATION(102, "所有下界合金镐耐久均不足10"),
NOT_ENOUGH_ITEMS(103, "物品不足"),
OUT_OF_RANGE(104, "超出操作距离"),
// ------------ 以下结果应当跳过任务 ------------
NO_PERMISSION(201, "跳过没有权限操作的方块"),
NOT_AIR_BLOCK(202, "跳过非空气方块"),
NO_BREAKABLE(203, "跳过不可破坏方块"),
;
private final int value;
private final String message;
JobErrCode(int i, String message) {
this.value = i;
this.message = message;
}
public int getValue() {
return value;
}
public String getMessage() {
return message;
}
public boolean canContinue() {
return value >= 200;
}
}

View File

@ -25,20 +25,24 @@ public class Place extends Job {
public JobErrCode Do() {
// 超出距离
if (!in_range(_creator, _location)) {
LoggerX.debug("超出距离!");
return JobErrCode.OUT_OF_RANGE;
}
// 跳过非空气方块
Block raw_block = _world.getBlockAt(_location);
if (!raw_block.isEmpty()) {
LoggerX.debug("目标方块不是空气!");
return JobErrCode.NOT_AIR_BLOCK;
}
// 获取到玩家物品中材料的第一个堆叠
int stack_index = _inventory.first(_block);
if (stack_index == -1) {
LoggerX.debug("玩家物品中没有该材料!");
return JobErrCode.NOT_ENOUGH_ITEMS;
}
ItemStack stack = _inventory.getItem(stack_index);
if (stack == null) {
LoggerX.debug("获取到的物品为空!");
return JobErrCode.NOT_ENOUGH_ITEMS;
}

View File

@ -12,6 +12,8 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import site.deercloud.liteworldedit.LoggerX;
import java.util.HashMap;
public class Remove extends Job {
public Remove(Location location, Player player) {
@ -26,36 +28,48 @@ public class Remove extends Job {
public JobErrCode Do() {
// 超出距离
if (!in_range(_creator, _location)) {
LoggerX.debug("超出距离!");
return JobErrCode.OUT_OF_RANGE;
}
Block raw_block = _world.getBlockAt(_location);
// 跳过不破坏的对象
if (raw_block.isLiquid() || raw_block.isEmpty() || raw_block.getType().getHardness() == -1) {
LoggerX.debug("目标方块是液体或空气或不可破坏!");
return JobErrCode.NO_BREAKABLE;
}
// 获取玩家背包中的下届合金镐
int stack_index = _inventory.first(Material.NETHERITE_PICKAXE);
if (stack_index == -1) {
HashMap<Integer, ?> pickaxes = _inventory.all(Material.NETHERITE_PICKAXE);
if (pickaxes.size() == 0) {
return JobErrCode.NO_PICKAXE;
}
ItemStack pickaxe = _inventory.getItem(stack_index);
if (pickaxe == null) {
LoggerX.debug("获取到的下界合金镐为空!");
return JobErrCode.NO_PICKAXE;
ItemStack pickaxe = null;
Damageable pickaxe_damage = null;
for (Integer index : pickaxes.keySet()) {
ItemStack p = _inventory.getItem(index);
if (p == null) {
LoggerX.debug(index + " 获取到的下界合金镐为空!");
continue;
}
ItemMeta pickaxe_meta = pickaxe.getItemMeta();
ItemMeta pickaxe_meta = p.getItemMeta();
if (pickaxe_meta == null) {
LoggerX.debug("获取到的下界合金镐元数据为空!");
return JobErrCode.NO_PICKAXE;
LoggerX.debug(index + " 获取到的下界合金镐元数据为空!");
continue;
}
if (!(pickaxe_meta instanceof Damageable)) {
LoggerX.debug("无法转换为Damageable");
return JobErrCode.NO_PICKAXE;
LoggerX.debug(index + " 无法转换为Damageable");
continue;
}
// 如果耐久小于10提示玩家
Damageable damageable = (Damageable) pickaxe_meta;
if (damageable.getDamage() >= 2031 - 10) {
LoggerX.err(_creator, "下界合金镐耐久太低!");
pickaxe_damage = (Damageable) pickaxe_meta;
if (pickaxe_damage.getDamage() >= 2031 - 10) {
LoggerX.debug(index + " 下界合金镐耐久太低!");
continue;
}
pickaxe = p;
break;
}
// 没有合适的镐
if (pickaxe == null) {
return JobErrCode.NOT_ENOUGH_DURATION;
}
BlockBreakEvent event = new BlockBreakEvent(raw_block, _creator);
@ -67,9 +81,8 @@ public class Remove extends Job {
double random = Math.random();
if (random < 1.0 / (durability + 1)) {
// 扣除耐久
damageable.setDamage(damageable.getDamage() + 1);
pickaxe.setItemMeta((ItemMeta) damageable);
pickaxe_damage.setDamage(pickaxe_damage.getDamage() + 1);
pickaxe.setItemMeta((ItemMeta) pickaxe_damage);
}
return JobErrCode.OK;
} else {

View File

@ -1,9 +1,11 @@
package site.deercloud.liteworldedit;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import site.deercloud.liteworldedit.Jobs.Job;
import site.deercloud.liteworldedit.Jobs.JobErrCode;
import site.deercloud.liteworldedit.Managers.ConfigManager;
import site.deercloud.liteworldedit.Managers.Cache;
@ -21,9 +23,30 @@ public final class LiteWorldEdit extends JavaPlugin {
new BukkitRunnable() {
@Override
public void run() {
Job job = _cache.get_one_job();
if (job != null) {
job.Do();
Job job = _cache.getOneJob();
if (job == null) {
return;
}
// 如果任务不可执行 允许在一个tick内多次执行直到任务可执行
int max_retries = 100;
JobErrCode re;
while ((re = job.Do()) != JobErrCode.OK) {
max_retries--;
if (max_retries <= 0) {
break;
}
if (re.canContinue()) {
job.get_creator().sendTitle("§c警告", "§c" + re.getMessage(), 10, 70, 20);
job = _cache.getOneJob();
if (job == null) {
return;
}
} else {
Player player = job.get_creator();
player.sendTitle("§c错误 任务已取消", "§c" + re.getMessage(), 10, 70, 20);
_cache.deleteAllJobsOfPlayer(player);
return;
}
}
}
}.runTaskTimer(this, 0, 1);

View File

@ -47,6 +47,7 @@ public class LoggerX {
}
public static void debug(String message) {
if (!_plugin.getConfigMgr().isDebug()) return;
_logger.info(ChatColor.BLUE + " D | " + message);
}
}

View File

@ -1,5 +1,9 @@
package site.deercloud.liteworldedit.Managers;
import org.bukkit.Bukkit;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Player;
import site.deercloud.liteworldedit.Jobs.Job;
@ -10,43 +14,87 @@ import java.util.Map;
public class Cache {
private final Map<String, Map<Integer, Point>> _points;
private final Map<String, LinkedList<Job>> _jobs;
private String _last_jobs_player; // 上一次被获取任务的玩家uuid
private final Map<String, BossBar> _bars;
public Cache() {
_points = new HashMap<String, Map<Integer, Point>>();
_jobs = new HashMap<String, LinkedList<Job>>();
_bars = new HashMap<String, BossBar>();
}
public void add_point(Player player, Integer index, Point point) {
public void addPoint(Player player, Integer index, Point point) {
if (!_points.containsKey(player.getUniqueId().toString())) {
_points.put(player.getUniqueId().toString(), new HashMap<Integer, Point>());
}
_points.get(player.getUniqueId().toString()).put(index, point);
}
public void add_job(Player player, Job job) {
public void addJob(Player player, Job job) {
if (!_jobs.containsKey(player.getUniqueId().toString())) {
_jobs.put(player.getUniqueId().toString(), new LinkedList<Job>());
}
_jobs.get(player.getUniqueId().toString()).add(job);
}
public Job get_one_job() {
for (Map.Entry<String, LinkedList<Job>> entry : _jobs.entrySet()) {
if (entry.getValue().size() > 0) {
return entry.getValue().removeFirst();
}
}
public Job getOneJob() {
String player = getNextPlayer();
if (player == null) {
return null;
}
public Map<Integer, Point> get_points(Player player) {
if (!_jobs.containsKey(player)) {
return null;
}
if (_jobs.get(player).isEmpty()) {
return null;
}
Job job = _jobs.get(player).pop();
updateBarOfPlayer(job.get_creator());
return job;
}
public Map<Integer, Point> getPoints(Player player) {
return _points.get(player.getUniqueId().toString());
}
public void delete_player_jobs(Player player) {
_jobs.remove(player.getUniqueId().toString());
public void deleteAllJobsOfPlayer(Player player) {
_jobs.get(player.getUniqueId().toString()).clear();
updateBarOfPlayer(player);
}
public void delete_player_points(Player player) {
public void deletePlayerCache(Player player) {
_points.remove(player.getUniqueId().toString());
_jobs.remove(player.getUniqueId().toString());
_bars.remove(player.getUniqueId().toString());
}
public void updateBarOfPlayer(Player player) {
if (!_bars.containsKey(player.getUniqueId().toString())) {
_bars.put(player.getUniqueId().toString(), Bukkit.createBossBar("§a§lLiteWorldEdit 施工进度", BarColor.GREEN, BarStyle.SOLID));
_bars.get(player.getUniqueId().toString()).addPlayer(player);
}
BossBar bar = _bars.get(player.getUniqueId().toString());
bar.setProgress(1);
bar.setTitle("§a§lLiteWorldEdit 施工进度 剩余任务: " + _jobs.get(player.getUniqueId().toString()).size());
bar.setVisible(_jobs.get(player.getUniqueId().toString()).size() != 0);
}
private String getNextPlayer() {
if (_jobs.size() == 0) {
return null;
}
if (_last_jobs_player == null) {
return _jobs.keySet().iterator().next();
}
boolean found = false;
for (String key : _jobs.keySet()) {
if (found) {
return key;
}
if (key.equals(_last_jobs_player)) {
found = true;
}
}
return _jobs.keySet().iterator().next();
}
}

View File

@ -0,0 +1,205 @@
package site.deercloud.liteworldedit.Managers;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarFlag;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Player;
import java.util.List;
public class ProgressBar implements BossBar {
/**
* Returns the title of this boss bar
*
* @return the title of the bar
*/
@Override
public String getTitle() {
return null;
}
/**
* Sets the title of this boss bar
*
* @param title the title of the bar
*/
@Override
public void setTitle(String title) {
}
/**
* Returns the color of this boss bar
*
* @return the color of the bar
*/
@Override
public BarColor getColor() {
return null;
}
/**
* Sets the color of this boss bar.
*
* @param color the color of the bar
*/
@Override
public void setColor(BarColor color) {
}
/**
* Returns the style of this boss bar
*
* @return the style of the bar
*/
@Override
public BarStyle getStyle() {
return null;
}
/**
* Sets the bar style of this boss bar
*
* @param style the style of the bar
*/
@Override
public void setStyle(BarStyle style) {
}
/**
* Remove an existing flag on this boss bar
*
* @param flag the existing flag to remove
*/
@Override
public void removeFlag(BarFlag flag) {
}
/**
* Add an optional flag to this boss bar
*
* @param flag an optional flag to set on the boss bar
*/
@Override
public void addFlag(BarFlag flag) {
}
/**
* Returns whether this boss bar as the passed flag set
*
* @param flag the flag to check
* @return whether it has the flag
*/
@Override
public boolean hasFlag(BarFlag flag) {
return false;
}
/**
* Sets the progress of the bar. Values should be between 0.0 (empty) and
* 1.0 (full)
*
* @param progress the progress of the bar
*/
@Override
public void setProgress(double progress) {
}
/**
* Returns the progress of the bar between 0.0 and 1.0
*
* @return the progress of the bar
*/
@Override
public double getProgress() {
return 0;
}
/**
* Adds the player to this boss bar causing it to display on their screen.
*
* @param player the player to add
*/
@Override
public void addPlayer(Player player) {
}
/**
* Removes the player from this boss bar causing it to be removed from their
* screen.
*
* @param player the player to remove
*/
@Override
public void removePlayer(Player player) {
}
/**
* Removes all players from this boss bar
*
* @see #removePlayer(Player)
*/
@Override
public void removeAll() {
}
/**
* Returns all players viewing this boss bar
*
* @return a immutable list of players
*/
@Override
public List<Player> getPlayers() {
return null;
}
/**
* Set if the boss bar is displayed to attached players.
*
* @param visible visible status
*/
@Override
public void setVisible(boolean visible) {
}
/**
* Return if the boss bar is displayed to attached players.
*
* @return visible status
*/
@Override
public boolean isVisible() {
return false;
}
/**
* Shows the previously hidden boss bar to all attached players
*
* @deprecated {@link #setVisible(boolean)}
*/
@Override
public void show() {
}
/**
* Hides this boss bar from all attached players
*
* @deprecated {@link #setVisible(boolean)}
*/
@Override
public void hide() {
}
}