实现了 Homes 相关指令
All checks were successful
Java CI-CD with Maven / build (push) Successful in 6m55s

This commit is contained in:
zhangyuheng 2024-05-13 16:57:09 +08:00
parent e051167757
commit d854a9b1ef
28 changed files with 942 additions and 186 deletions

View File

@ -1,6 +1,11 @@
package cn.lunadeer.essentialsd; package cn.lunadeer.essentialsd;
import cn.lunadeer.essentialsd.commands.*; import cn.lunadeer.essentialsd.commands.*;
import cn.lunadeer.essentialsd.commands.home.DelHome;
import cn.lunadeer.essentialsd.commands.home.Home;
import cn.lunadeer.essentialsd.commands.home.Homes;
import cn.lunadeer.essentialsd.commands.home.SetHome;
import cn.lunadeer.essentialsd.events.*;
import cn.lunadeer.essentialsd.managers.ConfigManager; import cn.lunadeer.essentialsd.managers.ConfigManager;
import cn.lunadeer.essentialsd.managers.DatabaseManager; import cn.lunadeer.essentialsd.managers.DatabaseManager;
import cn.lunadeer.essentialsd.managers.TeleportManager; import cn.lunadeer.essentialsd.managers.TeleportManager;
@ -27,8 +32,14 @@ public final class EssentialsD extends JavaPlugin {
scheduler = new Scheduler(this); scheduler = new Scheduler(this);
tpManager = new TeleportManager(); tpManager = new TeleportManager();
Bukkit.getPluginManager().registerEvents(new Events(), this); Bukkit.getPluginManager().registerEvents(new InvisibleItemFrameEvent(), this);
Bukkit.getPluginManager().registerEvents(new ChairEvent(), this); Bukkit.getPluginManager().registerEvents(new ChairEvent(), this);
Bukkit.getPluginManager().registerEvents(new ArmorStandHandsEvent(), this);
Bukkit.getPluginManager().registerEvents(new CrowEvent(), this);
Bukkit.getPluginManager().registerEvents(new ExpBottleEvent(), this);
Bukkit.getPluginManager().registerEvents(new ShowItemEvent(), this);
Bukkit.getPluginManager().registerEvents(new TeleportEvent(), this);
Bukkit.getPluginManager().registerEvents(new PlayerRecordEvent(), this);
// op 指令 // op 指令
Objects.requireNonNull(Bukkit.getPluginCommand("fly")).setExecutor(new Fly()); Objects.requireNonNull(Bukkit.getPluginCommand("fly")).setExecutor(new Fly());
Objects.requireNonNull(Bukkit.getPluginCommand("god")).setExecutor(new God()); Objects.requireNonNull(Bukkit.getPluginCommand("god")).setExecutor(new God());
@ -50,6 +61,10 @@ public final class EssentialsD extends JavaPlugin {
Objects.requireNonNull(Bukkit.getPluginCommand("tpa")).setExecutor(new Tpa()); Objects.requireNonNull(Bukkit.getPluginCommand("tpa")).setExecutor(new Tpa());
Objects.requireNonNull(Bukkit.getPluginCommand("rtp")).setExecutor(new Rtp()); Objects.requireNonNull(Bukkit.getPluginCommand("rtp")).setExecutor(new Rtp());
Objects.requireNonNull(Bukkit.getPluginCommand("back")).setExecutor(new Back()); Objects.requireNonNull(Bukkit.getPluginCommand("back")).setExecutor(new Back());
Objects.requireNonNull(Bukkit.getPluginCommand("home")).setExecutor(new Home());
Objects.requireNonNull(Bukkit.getPluginCommand("homes")).setExecutor(new Homes());
Objects.requireNonNull(Bukkit.getPluginCommand("sethome")).setExecutor(new SetHome());
Objects.requireNonNull(Bukkit.getPluginCommand("delhome")).setExecutor(new DelHome());
if (config.getRecipesCrowbar()) { if (config.getRecipesCrowbar()) {

View File

@ -0,0 +1,62 @@
package cn.lunadeer.essentialsd.commands.home;
import cn.lunadeer.essentialsd.EssentialsD;
import cn.lunadeer.essentialsd.dtos.HomeInfo;
import cn.lunadeer.essentialsd.tuis.HomeList;
import cn.lunadeer.essentialsd.utils.Notification;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class DelHome implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
Notification.warn(sender, "只有玩家可以使用此命令");
return true;
}
if (!EssentialsD.config.getCommandsHome()) {
Notification.error(sender, "这个命令已被管理员禁用");
return false;
}
Player player = (Player) sender;
String homeName;
if (args.length == 0) {
homeName = "default";
} else {
homeName = args[0];
}
boolean res = HomeInfo.deleteHome(player.getUniqueId(), homeName);
if (res) {
Notification.info(player, "成功删除家 " + homeName);
} else {
Notification.error(player, "删除家 " + homeName + " 失败, 请联系管理员");
}
if (args.length == 2) {
try {
int page = Integer.parseInt(args[1]);
String[] newArgs = new String[1];
newArgs[0] = String.valueOf(page);
HomeList.show(sender, newArgs);
} catch (Exception ignored) {
}
}
return true;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
List<HomeInfo> homes = HomeInfo.getHomesOf(((Player) sender).getUniqueId());
List<String> res = new ArrayList<>();
for (HomeInfo home : homes) {
res.add(home.homeName);
}
return res;
}
}

View File

@ -0,0 +1,60 @@
package cn.lunadeer.essentialsd.commands.home;
import cn.lunadeer.essentialsd.EssentialsD;
import cn.lunadeer.essentialsd.dtos.HomeInfo;
import cn.lunadeer.essentialsd.utils.Notification;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class Home implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
Notification.warn(sender, "只有玩家可以使用此命令");
return true;
}
if (!EssentialsD.config.getCommandsHome()) {
Notification.error(sender, "这个命令已被管理员禁用");
return false;
}
Player player = (Player) sender;
String homeName;
if (args.length == 0) {
homeName = "default";
} else {
homeName = args[0];
}
HomeInfo home = HomeInfo.getHome(player.getUniqueId(), homeName);
if (home == null) {
Notification.error(player, "不存在名为 " + homeName + " 的家");
return true;
}
try {
EssentialsD.tpManager.doTeleportDelayed(player, home.location, EssentialsD.config.getTpDelay(), () -> {
Notification.info(player, "正在传送到家 " + homeName);
}, () -> {
Notification.info(player, "成功传送到家 " + homeName);
});
} catch (RuntimeException e) {
Notification.error(player, "传送到家 " + homeName + " 失败: " + e.getMessage());
}
return true;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
List<HomeInfo> homes = HomeInfo.getHomesOf(((Player) sender).getUniqueId());
List<String> res = new ArrayList<>();
for (HomeInfo home : homes) {
res.add(home.homeName);
}
return res;
}
}

View File

@ -0,0 +1,36 @@
package cn.lunadeer.essentialsd.commands.home;
import cn.lunadeer.essentialsd.EssentialsD;
import cn.lunadeer.essentialsd.dtos.HomeInfo;
import cn.lunadeer.essentialsd.tuis.HomeList;
import cn.lunadeer.essentialsd.utils.Notification;
import cn.lunadeer.essentialsd.utils.STUI.Button;
import cn.lunadeer.essentialsd.utils.STUI.Line;
import cn.lunadeer.essentialsd.utils.STUI.ListView;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class Homes implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!EssentialsD.config.getCommandsHome()) {
Notification.error(sender, "这个命令已被管理员禁用");
return false;
}
HomeList.show(sender, args);
return true;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
return null;
}
}

View File

@ -0,0 +1,58 @@
package cn.lunadeer.essentialsd.commands.home;
import cn.lunadeer.essentialsd.EssentialsD;
import cn.lunadeer.essentialsd.dtos.HomeInfo;
import cn.lunadeer.essentialsd.utils.Notification;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class SetHome implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
Notification.warn(sender, "只有玩家可以使用此命令");
return true;
}
if (!EssentialsD.config.getCommandsHome()) {
Notification.error(sender, "这个命令已被管理员禁用");
return false;
}
Player player = (Player) sender;
List<HomeInfo> homes = HomeInfo.getHomesOf(((Player) sender).getUniqueId());
if (homes.size() > EssentialsD.config.getHomeLimitAmount()) {
Notification.error(player, "你的家数量已达上限");
return true;
}
HomeInfo info = new HomeInfo();
info.uuid = player.getUniqueId();
if (args.length == 0) {
info.homeName = "default";
} else {
info.homeName = args[0];
}
info.location = player.getLocation();
HomeInfo exist = HomeInfo.getHome(player.getUniqueId(), info.homeName);
if (exist != null) {
Notification.error(player, "已经存在名为 " + info.homeName + " 的家");
return true;
}
boolean res = HomeInfo.newHome(info);
if (res) {
Notification.info(player, "成功设置家 " + info.homeName);
} else {
Notification.error(player, "设置家 " + info.homeName + " 失败, 请联系管理员");
}
return true;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
return null;
}
}

View File

@ -0,0 +1,85 @@
package cn.lunadeer.essentialsd.dtos;
import cn.lunadeer.essentialsd.managers.DatabaseManager;
import cn.lunadeer.essentialsd.utils.LocationUtils;
import cn.lunadeer.essentialsd.utils.XLogger;
import org.bukkit.Location;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class HomeInfo {
/*
"CREATE TABLE IF NOT EXISTS home_info (" +
" id SERIAL PRIMARY KEY," +
" uuid VARCHAR(36) NOT NULL," +
" home_name TEXT NOT NULL," +
" location TEXT NOT NULL," +
" FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"
*/
public UUID uuid;
public String homeName;
public Location location;
public static boolean newHome(HomeInfo info) {
String sql = "INSERT INTO home_info (uuid, home_name, location) " +
"VALUES " +
"('" + info.uuid.toString() + "', '" + info.homeName + "', '" + LocationUtils.Serialize(info.location) + "')";
try (ResultSet rs = DatabaseManager.query(sql)) {
return true;
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
return false;
}
}
public static boolean deleteHome(UUID uuid, String homeName) {
String sql = "DELETE FROM home_info WHERE uuid = '" + uuid.toString() + "' AND home_name = '" + homeName + "'";
try (ResultSet rs = DatabaseManager.query(sql)) {
return true;
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
return false;
}
}
public static List<HomeInfo> getHomesOf(UUID uuid) {
List<HomeInfo> homes = new ArrayList<>();
String sql = "SELECT * FROM home_info WHERE uuid = '" + uuid.toString() + "'";
try (ResultSet rs = DatabaseManager.query(sql)) {
if (rs == null) return homes;
while (rs.next()) {
HomeInfo home = new HomeInfo();
home.uuid = UUID.fromString(rs.getString("uuid"));
home.homeName = rs.getString("home_name");
home.location = LocationUtils.Deserialize(rs.getString("location"));
homes.add(home);
}
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
}
return homes;
}
public static HomeInfo getHome(UUID uuid, String homeName) {
String sql = "SELECT * FROM home_info WHERE uuid = '" + uuid.toString() + "' AND home_name = '" + homeName + "'";
try (ResultSet rs = DatabaseManager.query(sql)) {
if (rs == null) return null;
if (rs.next()) {
HomeInfo home = new HomeInfo();
home.uuid = UUID.fromString(rs.getString("uuid"));
home.homeName = rs.getString("home_name");
home.location = LocationUtils.Deserialize(rs.getString("location"));
return home;
}
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
}
return null;
}
}

View File

@ -0,0 +1,48 @@
package cn.lunadeer.essentialsd.dtos;
import cn.lunadeer.essentialsd.managers.DatabaseManager;
import cn.lunadeer.essentialsd.utils.LocationUtils;
import cn.lunadeer.essentialsd.utils.XLogger;
import org.bukkit.entity.Player;
import java.net.InetSocketAddress;
import java.sql.ResultSet;
import java.util.UUID;
public class LoginRecord {
/*
"CREATE TABLE IF NOT EXISTS login_record (" +
" id SERIAL PRIMARY KEY," +
" uuid VARCHAR(36) NOT NULL," +
" ip VARCHAR(15) NOT NULL," +
" name TEXT NOT NULL," +
" login_time TIMESTAMP NOT NULL," +
" logout_location TEXT," +
" logout_time TIMESTAMP," +
" FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"
*/
public static void newLoginRecord(Player player) {
UUID uuid = player.getUniqueId();
InetSocketAddress address = player.getAddress();
String ip;
if (address == null) {
ip = "unknown";
} else {
ip = address.getAddress().getHostAddress();
}
long login_time = player.getLastLogin();
long logout_time = System.currentTimeMillis();
String logout_location = LocationUtils.Serialize(player.getLocation());
String sql = "INSERT INTO login_record (uuid, ip, login_time, logout_location, logout_time) " +
"VALUES " +
"('" + uuid.toString() + "', '" + ip + "', '" + login_time + "', '" + logout_location + "', '" + logout_time + "')";
try (ResultSet rs = DatabaseManager.query(sql)) {
if (rs == null) return;
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,64 @@
package cn.lunadeer.essentialsd.dtos;
import cn.lunadeer.essentialsd.managers.DatabaseManager;
import cn.lunadeer.essentialsd.utils.XLogger;
import com.destroystokyo.paper.profile.PlayerProfile;
import org.bukkit.entity.Player;
import java.net.InetSocketAddress;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class NameRecord {
/*
"CREATE TABLE IF NOT EXISTS name_record (" +
" id SERIAL PRIMARY KEY," +
" uuid VARCHAR(36) NOT NULL," +
" name TEXT NOT NULL," +
" time TIMESTAMP NOT NULL," +
" FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"
*/
public UUID uuid;
public String name;
public Long time;
public String timeString;
public static boolean newNameRecord(Player player){
UUID uuid = player.getUniqueId();
String name = player.getName();
String sql = "INSERT INTO name_record (uuid, name, time) VALUES ('" + uuid.toString() + "', '" + name + "', '" + System.currentTimeMillis() + "')";
try (ResultSet rs = DatabaseManager.query(sql)) {
return true;
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
return false;
}
}
public static List<NameRecord> get5NameHistoryOf(UUID uuid) {
return getNameHistoryOf(uuid, 5);
}
public static List<NameRecord> getNameHistoryOf(UUID uuid, Integer limit) {
List<NameRecord> records = new ArrayList<>();
String sql = "SELECT * FROM name_record WHERE uuid = '" + uuid.toString() + "' ORDER BY time DESC LIMIT " + limit;
try (ResultSet rs = DatabaseManager.query(sql)) {
if (rs == null) return records;
while (rs.next()) {
NameRecord record = new NameRecord();
record.uuid = UUID.fromString(rs.getString("uuid"));
record.name = rs.getString("name");
record.time = rs.getTimestamp("time").getTime();
record.timeString = rs.getTimestamp("time").toString();
records.add(record);
}
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
}
return records;
}
}

View File

@ -0,0 +1,42 @@
package cn.lunadeer.essentialsd.dtos;
import cn.lunadeer.essentialsd.managers.DatabaseManager;
import cn.lunadeer.essentialsd.utils.XLogger;
import java.sql.ResultSet;
import java.util.UUID;
public class PlayerName {
public static String getName(UUID uuid) {
String sql = "SELECT last_known_name FROM player_name WHERE uuid = '" + uuid.toString() + "'";
try (ResultSet rs = DatabaseManager.query(sql)) {
if (rs == null) return null;
if (rs.next()) {
return rs.getString("last_known_name");
}
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
}
return null;
}
public static boolean setName(UUID uuid, String name) {
String sql = "INSERT INTO player_name (uuid, last_known_name) VALUES ('" + uuid.toString() + "', '" + name + "') ON CONFLICT (uuid) DO UPDATE SET last_known_name = '" + name + "'";
try (ResultSet rs = DatabaseManager.query(sql)) {
return true;
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
return false;
}
}
public static boolean updateName(UUID uuid, String name) {
String sql = "UPDATE player_name SET last_known_name = '" + name + "' WHERE uuid = '" + uuid.toString() + "'";
try (ResultSet rs = DatabaseManager.query(sql)) {
return true;
} catch (Exception e) {
XLogger.err("Database query failed: " + e.getMessage());
return false;
}
}
}

View File

@ -0,0 +1,36 @@
package cn.lunadeer.essentialsd.events;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
public class ArmorStandHandsEvent implements Listener {
// put stick as arms for an ArmorStand
@EventHandler
public void onAddArmsForArmStand(EntityDamageByEntityEvent event) {
Entity entity = event.getEntity();
if (!(entity instanceof ArmorStand)) {
return;
}
if (!(event.getDamager() instanceof Player)) {
return;
}
Player player = (Player) event.getDamager();
ItemStack mainHand = player.getInventory().getItemInMainHand();
if (mainHand.getType() != Material.STICK) {
return;
}
event.setCancelled(true);
ArmorStand armorStand = (ArmorStand) entity;
if (armorStand.hasArms()) {
return;
}
armorStand.setArms(true);
mainHand.setAmount(mainHand.getAmount() - 1);
}
}

View File

@ -1,5 +1,6 @@
package cn.lunadeer.essentialsd; package cn.lunadeer.essentialsd.events;
import cn.lunadeer.essentialsd.EssentialsD;
import cn.lunadeer.essentialsd.utils.XLogger; import cn.lunadeer.essentialsd.utils.XLogger;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.Block; import org.bukkit.block.Block;

View File

@ -0,0 +1,57 @@
package cn.lunadeer.essentialsd.events;
import cn.lunadeer.essentialsd.EssentialsD;
import cn.lunadeer.essentialsd.recipes.Crowbar;
import cn.lunadeer.essentialsd.utils.Notification;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Rail;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import java.util.Objects;
public class CrowEvent implements Listener {
// change Rail's state with Crowbar
@EventHandler(priority = EventPriority.HIGHEST)
public void onCrowbarUse(PlayerInteractEvent event) {
Player player = event.getPlayer();
ItemStack mainHand = player.getInventory().getItemInMainHand();
if (event.getHand() == EquipmentSlot.OFF_HAND) {
return;
}
if (mainHand.getType() != Crowbar.getItemStack().getType()) {
return;
}
if (!Objects.requireNonNull(mainHand.getItemMeta()).getPersistentDataContainer().has(new NamespacedKey(EssentialsD.instance, "this_is_crowbar"), PersistentDataType.BYTE)) {
return;
}
if (event.getClickedBlock() == null) {
return;
}
Block block = event.getClickedBlock();
if (block.getType() == Material.POWERED_RAIL || block.getType() == Material.DETECTOR_RAIL || block.getType() == Material.ACTIVATOR_RAIL) {
return;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Rail)) {
return;
}
Rail rail = (Rail) blockData;
if (!Crowbar.changeable(rail.getShape())) {
Notification.warn(player, "无法使用撬棍修改此铁轨的方向");
return;
}
rail.setShape(Crowbar.changeToNext(rail.getShape()));
block.setBlockData(rail);
event.setCancelled(true);
}
}

View File

@ -0,0 +1,18 @@
package cn.lunadeer.essentialsd.events;
import cn.lunadeer.essentialsd.EssentialsD;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
public class ExpBottleEvent implements Listener {
// on throw ExpBottle set multiplier for ExpBottle
@EventHandler(priority = EventPriority.HIGHEST)
public void onExpBottleUsage(org.bukkit.event.entity.ExpBottleEvent event) {
int exp = event.getExperience();
if (exp < 0) {
event.setExperience(0);
}
event.setExperience((int) (exp * EssentialsD.config.getExpBottleRatio()));
}
}

View File

@ -1,53 +1,23 @@
package cn.lunadeer.essentialsd; package cn.lunadeer.essentialsd.events;
import cn.lunadeer.essentialsd.commands.ShowItem; import cn.lunadeer.essentialsd.EssentialsD;
import cn.lunadeer.essentialsd.recipes.Crowbar;
import cn.lunadeer.essentialsd.recipes.InvisibleGlowItemFrame; import cn.lunadeer.essentialsd.recipes.InvisibleGlowItemFrame;
import cn.lunadeer.essentialsd.recipes.InvisibleItemFrame; import cn.lunadeer.essentialsd.recipes.InvisibleItemFrame;
import cn.lunadeer.essentialsd.utils.Notification;
import cn.lunadeer.essentialsd.utils.XLogger;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Rail;
import org.bukkit.entity.*; import org.bukkit.entity.*;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.ExpBottleEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.hanging.HangingPlaceEvent; import org.bukkit.event.hanging.HangingPlaceEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import java.util.Objects; import java.util.Objects;
public class Events implements Listener { public class InvisibleItemFrameEvent implements Listener {
// cancel click ShowItem
@EventHandler
public void onShowItemClick(InventoryClickEvent event) {
Inventory inv = event.getClickedInventory();
if (inv == null) {
return;
}
InventoryView view = event.getView();
if (!ShowItem.cache.containsKey(view.getOriginalTitle())) {
return;
}
event.setCancelled(true);
}
// place an Invisible Item Frame // place an Invisible Item Frame
@EventHandler @EventHandler
public void placeItemFrame(HangingPlaceEvent event) { public void placeItemFrame(HangingPlaceEvent event) {
@ -127,80 +97,4 @@ public class Events implements Listener {
itemFrame.setVisible(true); itemFrame.setVisible(true);
} }
} }
// put stick as arms for an ArmorStand
@EventHandler
public void onAddArmsForArmStand(EntityDamageByEntityEvent event) {
Entity entity = event.getEntity();
if (!(entity instanceof ArmorStand)) {
return;
}
if (!(event.getDamager() instanceof Player)) {
return;
}
Player player = (Player) event.getDamager();
ItemStack mainHand = player.getInventory().getItemInMainHand();
if (mainHand.getType() != Material.STICK) {
return;
}
event.setCancelled(true);
ArmorStand armorStand = (ArmorStand) entity;
if (armorStand.hasArms()) {
return;
}
armorStand.setArms(true);
mainHand.setAmount(mainHand.getAmount() - 1);
}
// change Rail's state with Crowbar
@EventHandler(priority = EventPriority.HIGHEST)
public void onCrowbarUse(PlayerInteractEvent event) {
Player player = event.getPlayer();
ItemStack mainHand = player.getInventory().getItemInMainHand();
if (event.getHand() == EquipmentSlot.OFF_HAND) {
return;
}
if (mainHand.getType() != Crowbar.getItemStack().getType()) {
return;
}
if (!Objects.requireNonNull(mainHand.getItemMeta()).getPersistentDataContainer().has(new NamespacedKey(EssentialsD.instance, "this_is_crowbar"), PersistentDataType.BYTE)) {
return;
}
if (event.getClickedBlock() == null) {
return;
}
Block block = event.getClickedBlock();
if (block.getType() == Material.POWERED_RAIL || block.getType() == Material.DETECTOR_RAIL || block.getType() == Material.ACTIVATOR_RAIL) {
return;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Rail)) {
return;
}
Rail rail = (Rail) blockData;
if (!Crowbar.changeable(rail.getShape())) {
Notification.warn(player, "无法使用撬棍修改此铁轨的方向");
return;
}
rail.setShape(Crowbar.changeToNext(rail.getShape()));
block.setBlockData(rail);
event.setCancelled(true);
}
// on throw ExpBottle set multiplier for ExpBottle
@EventHandler(priority = EventPriority.HIGHEST)
public void onExpBottleUsage(ExpBottleEvent event) {
int exp = event.getExperience();
if (exp < 0) {
event.setExperience(0);
}
event.setExperience((int) (exp * EssentialsD.config.getExpBottleRatio()));
}
// on player death update tpManager
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getEntity();
EssentialsD.tpManager.updateLastTpLocation(player);
}
} }

View File

@ -0,0 +1,33 @@
package cn.lunadeer.essentialsd.events;
import cn.lunadeer.essentialsd.dtos.LoginRecord;
import cn.lunadeer.essentialsd.dtos.NameRecord;
import cn.lunadeer.essentialsd.dtos.PlayerName;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class PlayerRecordEvent implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLogin(PlayerLoginEvent event) {
Player player = event.getPlayer();
String last_name = PlayerName.getName(player.getUniqueId());
if (last_name == null) {
PlayerName.setName(player.getUniqueId(), player.getName());
NameRecord.newNameRecord(player);
} else if (!last_name.equals(player.getName())) {
PlayerName.updateName(player.getUniqueId(), player.getName());
NameRecord.newNameRecord(player);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLogout(PlayerQuitEvent event) {
Player player = event.getPlayer();
LoginRecord.newLoginRecord(player);
}
}

View File

@ -0,0 +1,24 @@
package cn.lunadeer.essentialsd.events;
import cn.lunadeer.essentialsd.commands.ShowItem;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
public class ShowItemEvent implements Listener {
// cancel click ShowItem
@EventHandler
public void onShowItemClick(InventoryClickEvent event) {
Inventory inv = event.getClickedInventory();
if (inv == null) {
return;
}
InventoryView view = event.getView();
if (!ShowItem.cache.containsKey(view.getOriginalTitle())) {
return;
}
event.setCancelled(true);
}
}

View File

@ -0,0 +1,17 @@
package cn.lunadeer.essentialsd.events;
import cn.lunadeer.essentialsd.EssentialsD;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
public class TeleportEvent implements Listener {
// on player death update tpManager
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getEntity();
EssentialsD.tpManager.updateLastTpLocation(player);
}
}

View File

@ -33,10 +33,12 @@ public class ConfigManager {
_commands_hat = _file.getBoolean("Commands.Hat", true); _commands_hat = _file.getBoolean("Commands.Hat", true);
_commands_showitem = _file.getBoolean("Commands.ShowItem", true); _commands_showitem = _file.getBoolean("Commands.ShowItem", true);
_commands_skull = _file.getBoolean("Commands.Skull", true); _commands_skull = _file.getBoolean("Commands.Skull", true);
_commands_home = _file.getBoolean("Commands.Home", true);
_tp_delay = _file.getInt("Teleport.Delay", 0); _tp_delay = _file.getInt("Teleport.Delay", 0);
_tp_cool_down = _file.getInt("Teleport.CoolDown", 0); _tp_cool_down = _file.getInt("Teleport.CoolDown", 0);
_tp_tpa_expire = _file.getInt("Teleport.TpaExpire", 30); _tp_tpa_expire = _file.getInt("Teleport.TpaExpire", 30);
_tp_rtp_radius = _file.getInt("Teleport.RtpRadius", 10000); _tp_rtp_radius = _file.getInt("Teleport.RtpRadius", 1000);
_tp_world_blacklist = _file.getStringList("Teleport.WorldBlackList");
_chair_enable = _file.getBoolean("Chair.Enable", true); _chair_enable = _file.getBoolean("Chair.Enable", true);
_chair_max_width = _file.getInt("Chair.MaxWidth", 4); _chair_max_width = _file.getInt("Chair.MaxWidth", 4);
_chair_sign_check = _file.getBoolean("Chair.SignCheck", true); _chair_sign_check = _file.getBoolean("Chair.SignCheck", true);
@ -45,6 +47,8 @@ public class ConfigManager {
_recipes_invisible_item_frame = _file.getBoolean("Recipes.InvisibleItemFrame", true); _recipes_invisible_item_frame = _file.getBoolean("Recipes.InvisibleItemFrame", true);
_recipes_light_block = _file.getBoolean("Recipes.LightBlock", true); _recipes_light_block = _file.getBoolean("Recipes.LightBlock", true);
_recipes_stacked_enchant_book = _file.getBoolean("Recipes.StackedEnchantBook", true); _recipes_stacked_enchant_book = _file.getBoolean("Recipes.StackedEnchantBook", true);
_home_limit_amount = _file.getInt("HomeLimit.Amount", 5);
_home_world_blacklist = _file.getStringList("HomeLimit.WorldBlacklist");
_db_type = _file.getString("Database.Type", "sqlite"); _db_type = _file.getString("Database.Type", "sqlite");
if (!_db_type.equals("pgsql") && !_db_type.equals("sqlite")) { if (!_db_type.equals("pgsql") && !_db_type.equals("sqlite")) {
XLogger.err("当前数据库只支持 pgsql 或 sqlite已重置为 sqlite"); XLogger.err("当前数据库只支持 pgsql 或 sqlite已重置为 sqlite");
@ -107,6 +111,10 @@ public class ConfigManager {
return _tp_rtp_radius; return _tp_rtp_radius;
} }
public List<String> getTpWorldBlackList() {
return _tp_world_blacklist;
}
public Boolean getCommandsTpa() { public Boolean getCommandsTpa() {
return _commands_tpa; return _commands_tpa;
} }
@ -139,6 +147,10 @@ public class ConfigManager {
return _commands_skull; return _commands_skull;
} }
public Boolean getCommandsHome() {
return _commands_home;
}
public Boolean getChairEnable() { public Boolean getChairEnable() {
return _chair_enable; return _chair_enable;
@ -210,6 +222,14 @@ public class ConfigManager {
}, _chunk_operate_delay * 20); }, _chunk_operate_delay * 20);
} }
public Integer getHomeLimitAmount() {
return _home_limit_amount;
}
public List<String> getHomeWorldBlacklist() {
return _home_world_blacklist;
}
public String getDbType() { public String getDbType() {
return _db_type; return _db_type;
} }
@ -269,6 +289,7 @@ public class ConfigManager {
private Integer _tp_delay; private Integer _tp_delay;
private Integer _tp_cool_down; private Integer _tp_cool_down;
private Integer _tp_rtp_radius; private Integer _tp_rtp_radius;
private List<String> _tp_world_blacklist;
private Boolean _chair_enable; private Boolean _chair_enable;
private Integer _chair_max_width; private Integer _chair_max_width;
private Boolean _chair_sign_check; private Boolean _chair_sign_check;
@ -283,12 +304,17 @@ public class ConfigManager {
private Boolean _commands_hat; private Boolean _commands_hat;
private Boolean _commands_showitem; private Boolean _commands_showitem;
private Boolean _commands_skull; private Boolean _commands_skull;
private Boolean _commands_home;
// recipes // recipes
private Boolean _recipes_crowbar; private Boolean _recipes_crowbar;
private Boolean _recipes_invisible_item_frame; private Boolean _recipes_invisible_item_frame;
private Boolean _recipes_light_block; private Boolean _recipes_light_block;
private Boolean _recipes_stacked_enchant_book; private Boolean _recipes_stacked_enchant_book;
// home limit
private Integer _home_limit_amount;
private List<String> _home_world_blacklist;
// database // database
private String _db_type; private String _db_type;
private String _db_host; private String _db_host;

View File

@ -102,10 +102,11 @@ public class DatabaseManager {
" id SERIAL PRIMARY KEY," + " id SERIAL PRIMARY KEY," +
" uuid VARCHAR(36) NOT NULL," + " uuid VARCHAR(36) NOT NULL," +
" ip VARCHAR(15) NOT NULL," + " ip VARCHAR(15) NOT NULL," +
" name TEXT NOT NULL," +
" login_time TIMESTAMP NOT NULL," + " login_time TIMESTAMP NOT NULL," +
" logout_location TEXT," + " logout_location TEXT," +
" logout_time TIMESTAMP" + " logout_time TIMESTAMP," +
" FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"; ");";
query(sql); query(sql);
@ -114,7 +115,9 @@ public class DatabaseManager {
" id SERIAL PRIMARY KEY," + " id SERIAL PRIMARY KEY," +
" uuid VARCHAR(36) NOT NULL," + " uuid VARCHAR(36) NOT NULL," +
" name TEXT NOT NULL," + " name TEXT NOT NULL," +
" time TIMESTAMP NOT NULL" + " time TIMESTAMP NOT NULL," +
" FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"; ");";
query(sql); query(sql);
@ -126,7 +129,9 @@ public class DatabaseManager {
" to_location TEXT NOT NULL," + " to_location TEXT NOT NULL," +
" type TEXT NOT NULL," + " type TEXT NOT NULL," +
" success BOOLEAN NOT NULL," + " success BOOLEAN NOT NULL," +
" time TIMESTAMP NOT NULL" + " time TIMESTAMP NOT NULL," +
" FOREIGN KEY (initiator_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"; ");";
query(sql); query(sql);
@ -135,7 +140,9 @@ public class DatabaseManager {
" id SERIAL PRIMARY KEY," + " id SERIAL PRIMARY KEY," +
" sender_uuid VARCHAR(36) NOT NULL," + " sender_uuid VARCHAR(36) NOT NULL," +
" message TEXT NOT NULL," + " message TEXT NOT NULL," +
" time TIMESTAMP NOT NULL" + " time TIMESTAMP NOT NULL," +
" FOREIGN KEY (sender_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"; ");";
query(sql); query(sql);
@ -144,7 +151,9 @@ public class DatabaseManager {
" id SERIAL PRIMARY KEY," + " id SERIAL PRIMARY KEY," +
" executor_uuid VARCHAR(36) NOT NULL," + " executor_uuid VARCHAR(36) NOT NULL," +
" command TEXT NOT NULL," + " command TEXT NOT NULL," +
" time TIMESTAMP NOT NULL" + " time TIMESTAMP NOT NULL," +
" FOREIGN KEY (executor_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"; ");";
query(sql); query(sql);
@ -153,7 +162,9 @@ public class DatabaseManager {
" id SERIAL PRIMARY KEY," + " id SERIAL PRIMARY KEY," +
" uuid VARCHAR(36) NOT NULL," + " uuid VARCHAR(36) NOT NULL," +
" home_name TEXT NOT NULL," + " home_name TEXT NOT NULL," +
" location TEXT NOT NULL" + " location TEXT NOT NULL," +
" FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
");"; ");";
query(sql); query(sql);
} }

View File

@ -18,6 +18,7 @@ import org.bukkit.event.player.PlayerTeleportEvent;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
public class TeleportManager { public class TeleportManager {
@ -44,14 +45,9 @@ public class TeleportManager {
return; return;
} }
LocalDateTime now = LocalDateTime.now(); if (EssentialsD.config.getTpWorldBlackList().contains(target.getWorld().getName())) {
LocalDateTime next_time = _next_time_allow_tp.get(initiator.getUniqueId()); Notification.error(initiator, "目的地所在世界 " + initiator.getWorld().getName() + " 不允许传送");
if (next_time != null) { return;
if (now.isBefore(next_time)) {
long secs_until_next = now.until(next_time, java.time.temporal.ChronoUnit.SECONDS);
Notification.error(initiator, "请等待 " + secs_until_next + " 秒后再次传送");
return;
}
} }
TpTask task = new TpTask(); TpTask task = new TpTask();
@ -70,7 +66,6 @@ public class TeleportManager {
Notification.info(target, Component.text("| ", main_color).append(acceptBtn).append(Component.text(" ", main_color)).append(denyBtn)); Notification.info(target, Component.text("| ", main_color).append(acceptBtn).append(Component.text(" ", main_color)).append(denyBtn));
Notification.info(target, Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color)); Notification.info(target, Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color));
_next_time_allow_tp.put(initiator.getUniqueId(), now.plusSeconds(EssentialsD.config.getTpCoolDown()));
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> {
_tasks.remove(task.taskId); _tasks.remove(task.taskId);
}, 20L * EssentialsD.config.getTpTpaExpire()); }, 20L * EssentialsD.config.getTpTpaExpire());
@ -110,17 +105,19 @@ public class TeleportManager {
if (!task.initiator.isOnline() || !task.target.isOnline()) { if (!task.initiator.isOnline() || !task.target.isOnline()) {
return; return;
} }
Notification.info(task.target, "已接受 " + task.initiator.getName() + " 的传送请求"); Notification.info(task.target, "已接受 " + task.initiator.getName() + " 的传送请求");
if (EssentialsD.config.getTpDelay() > 0) { Notification.info(task.initiator, "玩家 " + task.target.getName() + " 已接受你的传送请求");
Notification.info(task.initiator, "玩家 " + task.target.getName() + " 已接受你的传送请求,传送将在 " + EssentialsD.config.getTpDelay() + " 秒后进行"); try {
} doTeleportDelayed(task.initiator, task.target.getLocation(), EssentialsD.config.getTpDelay(), () -> {
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { Notification.info(task.initiator, "正在传送到 " + task.initiator.getName() + " 的位置");
if (task.initiator.isOnline() && task.target.isOnline()) { }, () -> {
doTeleportSafely(task.initiator, task.target.getLocation()); Notification.info(task.initiator, "已传送到 " + task.initiator.getName() + " 的位置");
Notification.info(task.initiator, "已传送到 " + task.target.getName() + " 的位置");
Notification.info(task.target, "玩家 " + task.initiator.getName() + " 已传送到你的位置"); Notification.info(task.target, "玩家 " + task.initiator.getName() + " 已传送到你的位置");
} });
}, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay()); } catch (RuntimeException e) {
Notification.error(player, e.getMessage());
}
} }
public void back(Player player) { public void back(Player player) {
@ -128,29 +125,68 @@ public class TeleportManager {
Notification.error(player, "没有找到可返回的位置"); Notification.error(player, "没有找到可返回的位置");
return; return;
} }
Location target = _last_tp_location.get(player.getUniqueId());
if (EssentialsD.config.getTpWorldBlackList().contains(target.getWorld().getName())) {
Notification.error(player, "目的地所在世界 " + target.getWorld().getName() + " 不允许传送");
return;
}
if (EssentialsD.config.getTpDelay() > 0) { if (EssentialsD.config.getTpDelay() > 0) {
Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后返回上次传送的位置"); Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后返回上次传送的位置");
} }
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { try {
doTeleportSafely(player, _last_tp_location.get(player.getUniqueId())); doTeleportDelayed(player, target, EssentialsD.config.getTpDelay(), () -> {
Notification.info(player, "已返回上次传送的位置"); Notification.info(player, "正在返回上次传送的位置");
}, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay()); }, () -> {
Notification.info(player, "已返回上次传送的位置");
});
} catch (RuntimeException e) {
Notification.error(player, e.getMessage());
}
} }
public void rtp(Player player) { public void rtp(Player player) {
if (EssentialsD.config.getTpWorldBlackList().contains(player.getWorld().getName())) {
Notification.error(player, "此世界 " + player.getWorld().getName() + " 不允许传送");
return;
}
int radius = EssentialsD.config.getTpRtpRadius(); int radius = EssentialsD.config.getTpRtpRadius();
World world = player.getWorld(); World world = player.getWorld();
int x = (int) (Math.random() * radius * 2) - radius + (int) player.getLocation().getX(); int x = (int) (Math.random() * radius * 2) - radius + (int) player.getLocation().getX();
int z = (int) (Math.random() * radius * 2) - radius + (int) player.getLocation().getZ(); int z = (int) (Math.random() * radius * 2) - radius + (int) player.getLocation().getZ();
XLogger.debug("RTP: " + x + " " + z); XLogger.debug("RTP: " + x + " " + z);
Location location = new Location(world, x + 0.5, player.getY(), z + 0.5); Location location = new Location(world, x + 0.5, player.getY(), z + 0.5);
if (EssentialsD.config.getTpDelay() > 0) { try {
Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后传送到随机位置"); doTeleportDelayed(player, location, EssentialsD.config.getTpDelay(), () -> {
Notification.info(player, "正在传送到随机位置");
}, () -> {
Notification.info(player, "已传送到随机位置");
});
} catch (RuntimeException e) {
Notification.error(player, e.getMessage());
} }
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { }
public void doTeleportDelayed(Player player, Location location, Integer delay, Runnable before, Runnable after) {
doTeleportDelayed(player, location, delay.longValue(), before, after);
}
public void doTeleportDelayed(Player player, Location location, Long delay, Runnable before, Runnable after) throws RuntimeException {
if (EssentialsD.config.getTpWorldBlackList().contains(location.getWorld().getName())) {
Notification.error(player, "目的地所在世界 " + location.getWorld().getName() + " 不允许传送");
return;
}
if (delay > 0) {
Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后执行传送");
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> {
before.run();
doTeleportSafely(player, location);
after.run();
}, 20L * delay);
} else {
before.run();
doTeleportSafely(player, location); doTeleportSafely(player, location);
Notification.info(player, "已传送到随机位置"); after.run();
}, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay()); }
} }
/** /**
@ -159,16 +195,23 @@ public class TeleportManager {
* @param player 玩家 * @param player 玩家
* @param location 位置 * @param location 位置
*/ */
public void doTeleportSafely(Player player, Location location) { public void doTeleportSafely(Player player, Location location) throws RuntimeException {
LocalDateTime now = LocalDateTime.now();
LocalDateTime next_time = _next_time_allow_tp.get(player.getUniqueId());
if (next_time != null) {
if (now.isBefore(next_time)) {
long secs_until_next = now.until(next_time, java.time.temporal.ChronoUnit.SECONDS);
throw new RuntimeException("请等待 " + secs_until_next + " 秒后再次传送");
}
}
_next_time_allow_tp.put(player.getUniqueId(), now.plusSeconds(EssentialsD.config.getTpCoolDown()));
location.getWorld().getChunkAtAsyncUrgently(location).thenAccept((chunk) -> { location.getWorld().getChunkAtAsyncUrgently(location).thenAccept((chunk) -> {
updateLastTpLocation(player);
int max_attempts = 512; int max_attempts = 512;
while (location.getBlock().isPassable()) { while (location.getBlock().isPassable()) {
location.setY(location.getY() - 1); location.setY(location.getY() - 1);
max_attempts--; max_attempts--;
if (max_attempts <= 0) { if (max_attempts <= 0) {
Notification.error(player, "传送目的地不安全,已取消传送"); throw new RuntimeException("传送目的地不安全,已取消传送");
return;
} }
} }
Block up1 = location.getBlock().getRelative(BlockFace.UP); Block up1 = location.getBlock().getRelative(BlockFace.UP);
@ -180,15 +223,14 @@ public class TeleportManager {
up2 = up1.getRelative(BlockFace.UP); up2 = up1.getRelative(BlockFace.UP);
max_attempts--; max_attempts--;
if (max_attempts <= 0) { if (max_attempts <= 0) {
Notification.error(player, "传送目的地不安全,已取消传送"); throw new RuntimeException("传送目的地不安全,已取消传送");
return;
} }
} }
location.setY(location.getY() + 1); location.setY(location.getY() + 1);
if (location.getBlock().getRelative(BlockFace.DOWN).getType() == Material.LAVA) { if (location.getBlock().getRelative(BlockFace.DOWN).getType() == Material.LAVA) {
Notification.error(player, "传送目的地不安全,已取消传送"); throw new RuntimeException("传送目的地不安全,已取消传送");
return;
} }
updateLastTpLocation(player);
player.teleportAsync(location, PlayerTeleportEvent.TeleportCause.PLUGIN); player.teleportAsync(location, PlayerTeleportEvent.TeleportCause.PLUGIN);
}); });
} }

View File

@ -0,0 +1,44 @@
package cn.lunadeer.essentialsd.tuis;
import cn.lunadeer.essentialsd.EssentialsD;
import cn.lunadeer.essentialsd.dtos.HomeInfo;
import cn.lunadeer.essentialsd.utils.Notification;
import cn.lunadeer.essentialsd.utils.STUI.Button;
import cn.lunadeer.essentialsd.utils.STUI.Line;
import cn.lunadeer.essentialsd.utils.STUI.ListView;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
public class HomeList {
public static void show(CommandSender sender, String[] args) {
if (!(sender instanceof Player)) {
Notification.warn(sender, "只有玩家可以使用此命令");
return;
}
Player player = (Player) sender;
List<HomeInfo> homes = HomeInfo.getHomesOf(((Player) sender).getUniqueId());
if (homes.size() == 0) {
Notification.warn(player, "你还没有设置家");
return;
}
int page = 1;
if (args.length == 1) {
try {
page = Integer.parseInt(args[0]);
} catch (Exception ignored) {
}
}
ListView view = ListView.create(5, "/homes");
view.title("Home 列表");
for (HomeInfo home : homes) {
Line line = Line.create()
.append(home.homeName)
.append(Button.create("传送", "/home " + home.homeName))
.append(Button.createRed("删除", "/delhome " + home.homeName + " " + page));
view.add(line);
}
view.showOn(player, page);
}
}

View File

@ -0,0 +1,21 @@
package cn.lunadeer.essentialsd.utils;
import org.bukkit.Location;
import org.bukkit.World;
public class LocationUtils {
public static String Serialize(Location loc) {
return loc.getWorld().getName() + "," + loc.getX() + "," + loc.getY() + "," + loc.getZ() + "," + loc.getYaw() + "," + loc.getPitch();
}
public static Location Deserialize(String loc) {
String[] parts = loc.split(",");
World world = org.bukkit.Bukkit.getWorld(parts[0]);
double x = Double.parseDouble(parts[1]);
double y = Double.parseDouble(parts[2]);
double z = Double.parseDouble(parts[3]);
float yaw = Float.parseFloat(parts[4]);
float pitch = Float.parseFloat(parts[5]);
return new Location(world, x, y, z, yaw, pitch);
}
}

View File

@ -8,14 +8,14 @@ import java.util.List;
public class Line { public class Line {
private String d = " - ";
private final List<Component> elements = new ArrayList<>(); private final List<Component> elements = new ArrayList<>();
private final TextComponent divider = Component.text(" - ", ViewStyles.sub_color);
public Line() { public Line() {
} }
public TextComponent build() { public TextComponent build() {
TextComponent divider = Component.text(d, ViewStyles.sub_color);
TextComponent.Builder builder = Component.text(); TextComponent.Builder builder = Component.text();
for (int i = 0; i < elements.size(); i++) { for (int i = 0; i < elements.size(); i++) {
builder.append(elements.get(i)); builder.append(elements.get(i));
@ -30,11 +30,20 @@ public class Line {
return new Line(); return new Line();
} }
List<Component> getElements() {
return elements;
}
public Line append(TextComponent component) { public Line append(TextComponent component) {
elements.add(component); elements.add(component);
return this; return this;
} }
public Line setDivider(String d) {
this.d = d;
return this;
}
public Line append(Component component) { public Line append(Component component) {
elements.add(component); elements.add(component);
return this; return this;
@ -45,4 +54,4 @@ public class Line {
return this; return this;
} }
} }

View File

@ -1,6 +1,8 @@
package cn.lunadeer.essentialsd.utils.STUI; package cn.lunadeer.essentialsd.utils.STUI;
import cn.lunadeer.essentialsd.utils.Notification; import cn.lunadeer.essentialsd.utils.Notification;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.ArrayList; import java.util.ArrayList;
@ -13,7 +15,7 @@ public class ListView {
private String command = ""; private String command = "";
private final View view = View.create(); private final View view = View.create();
public ListView(int page_size, String command) { private ListView(int page_size, String command) {
super(); super();
this.page_size = page_size; this.page_size = page_size;
this.command = command; this.command = command;
@ -39,6 +41,16 @@ public class ListView {
return this; return this;
} }
public ListView subtitle(TextComponent subtitle) {
view.subtitle(subtitle);
return this;
}
public ListView subtitle(Line line) {
view.subtitle(line);
return this;
}
public ListView add(Line line) { public ListView add(Line line) {
lines.add(line); lines.add(line);
return this; return this;
@ -61,11 +73,23 @@ public class ListView {
} }
for (int i = offset; i < offset + page_size; i++) { for (int i = offset; i < offset + page_size; i++) {
if (i >= lines.size()) { if (i >= lines.size()) {
for (int j = 0; j < page_size - lines.size() % page_size; j++) {
view.addLine(Line.create());
}
break; break;
} }
view.addLine(lines.get(i)); view.addLine(lines.get(i));
} }
view.actionBar(Pagination.create(page, lines.size(), this.command)); view.actionBar(Pagination.create(page, lines.size(), page_size, this.command));
view.showOn(player); view.showOn(player);
} }
}
public ListView navigator(Line line) {
Line nav = Line.create().setDivider("->").append(Component.text("导航", ViewStyles.main_color));
for (Component component : line.getElements()) {
nav.append(component);
}
view.subtitle(nav);
return this;
}
}

View File

@ -11,9 +11,8 @@ import static cn.lunadeer.essentialsd.utils.STUI.ViewStyles.sub_color;
public class Pagination { public class Pagination {
public static TextComponent create(int page, int item_size, String command) { public static TextComponent create(int page, int item_size, int page_size, String command) {
// x/y [上一页] [下一页] // x/y [上一页] [下一页]
int page_size = 4;
int page_count = (int) Math.ceil((double) item_size / page_size); int page_count = (int) Math.ceil((double) item_size / page_size);
if (page_count == 0) { if (page_count == 0) {
page_count = 1; page_count = 1;
@ -26,9 +25,13 @@ public class Pagination {
componentList.add(Component.text("", main_color)); componentList.add(Component.text("", main_color));
if (page > 1) { if (page > 1) {
componentList.add(Button.create("上一页", command + " " + (page - 1))); componentList.add(Button.create("上一页", command + " " + (page - 1)));
} else {
componentList.add(Component.text("[上一页]", sub_color));
} }
if (page < page_count) { if (page < page_count) {
componentList.add(Button.create("下一页", command + " " + (page + 1))); componentList.add(Button.create("下一页", command + " " + (page + 1)));
} else {
componentList.add(Component.text("[下一页]", sub_color));
} }
TextComponent.Builder builder = Component.text(); TextComponent.Builder builder = Component.text();
for (Component component : componentList) { for (Component component : componentList) {

View File

@ -10,33 +10,25 @@ import java.util.List;
import static cn.lunadeer.essentialsd.utils.STUI.ViewStyles.main_color; import static cn.lunadeer.essentialsd.utils.STUI.ViewStyles.main_color;
public class View { public class View {
protected TextComponent title_decorate = Component.text("", main_color);
protected TextComponent space = Component.text(" "); protected TextComponent space = Component.text(" ");
protected TextComponent sub_title_decorate = Component.text("- ", main_color); protected TextComponent sub_title_decorate = Component.text("- ", main_color);
protected TextComponent line_decorate = Component.text("", main_color); protected TextComponent line_decorate = Component.text("", main_color);
protected TextComponent action_decorate = Component.text("", main_color); protected TextComponent action_decorate = Component.text("", main_color);
protected TextComponent title = Component.text(" "); protected TextComponent title = Component.text(" ");
protected TextComponent subtitle = Component.text(""); protected TextComponent subtitle = null;
protected List<TextComponent> content_lines = new ArrayList<>(); protected List<TextComponent> content_lines = new ArrayList<>();
protected TextComponent actionbar = Component.text(" "); protected TextComponent actionbar = null;
protected TextComponent edge = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color); protected TextComponent edge = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color);
protected TextComponent divide_line = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color); protected TextComponent divide_line = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color);
public void showOn(Player player) { public void showOn(Player player) {
player.sendMessage(edge); // player.sendMessage(edge);
TextComponent.Builder builder = Component.text(); TextComponent.Builder builder = Component.text();
int title_length = title.content().length(); builder.append(Component.text("__/", main_color));
int title_width = title_length * 2 + 2;
int decorate_count = divide_line.content().length() - title_width;
for (int i = 0; i < decorate_count / 2; i++) {
builder.append(title_decorate);
}
builder.append(space).append(title).append(space); builder.append(space).append(title).append(space);
for (int i = 0; i < decorate_count / 2; i++) { builder.append(Component.text("\\__", main_color));
builder.append(title_decorate);
}
player.sendMessage(builder.build()); player.sendMessage(builder.build());
if (subtitle.content().length() > 0) { if (subtitle != null) {
player.sendMessage(divide_line); player.sendMessage(divide_line);
player.sendMessage(Component.text().append(sub_title_decorate).append(subtitle).build()); player.sendMessage(Component.text().append(sub_title_decorate).append(subtitle).build());
} }
@ -44,8 +36,10 @@ public class View {
for (TextComponent content_line : content_lines) { for (TextComponent content_line : content_lines) {
player.sendMessage(Component.text().append(line_decorate).append(content_line).build()); player.sendMessage(Component.text().append(line_decorate).append(content_line).build());
} }
player.sendMessage(divide_line); if (actionbar != null) {
player.sendMessage(Component.text().append(action_decorate).append(actionbar).build()); player.sendMessage(divide_line);
player.sendMessage(Component.text().append(action_decorate).append(actionbar).build());
}
player.sendMessage(edge); player.sendMessage(edge);
player.sendMessage(Component.text(" ")); player.sendMessage(Component.text(" "));
} }
@ -69,6 +63,19 @@ public class View {
return this; return this;
} }
public View subtitle(Line line) {
this.subtitle = line.build();
return this;
}
public View navigator(Line line) {
Line nav = Line.create().setDivider("->").append(Component.text("导航", ViewStyles.main_color));
for (Component component : line.getElements()) {
nav.append(component);
}
return this.subtitle(nav);
}
public View subtitle(TextComponent subtitle) { public View subtitle(TextComponent subtitle) {
this.subtitle = subtitle; this.subtitle = subtitle;
return this; return this;
@ -103,4 +110,4 @@ public class View {
this.content_lines.add(component.build()); this.content_lines.add(component.build());
return this; return this;
} }
} }

View File

@ -37,13 +37,15 @@ Commands:
Tpa: true # 传送到其他玩家 Tpa: true # 传送到其他玩家
Rtp: true # 随机传送到附近 Rtp: true # 随机传送到附近
Back: true # 回到上一个传送位置 Back: true # 回到上一个传送位置
Home: true # home相关指令 包含 sethome delhome home homes
# 传送 # 传送
Teleport: Teleport:
Delay: 0 # 传送延迟 秒 Delay: 0 # 传送延迟 秒
CoolDown: 0 # 冷却时间 秒 CoolDown: 0 # 冷却时间 秒
TpaExpire: 30 # 传送请求有效期 秒 TpaExpire: 30 # 传送请求有效期 秒
RtpRadius: 1000 # 随机传送最大半径 RtpRadius: 1000 # 随机传送最大半径
WorldBlackList: [ ] # 不允许传送的世界
# 把楼梯当作椅子使用 # 把楼梯当作椅子使用
Chair: Chair:
@ -52,6 +54,11 @@ Chair:
SignCheck: false SignCheck: false
SitHeight: -0.95 SitHeight: -0.95
# home 相关限制
HomeLimit:
Amount: 5 # 数量限制
WorldBlackList: [ ] # 不允许设置 home 的世界
Debug: false Debug: false
CheckUpdate: true CheckUpdate: true

View File

@ -63,4 +63,16 @@ commands:
usage: /back usage: /back
inspect: inspect:
description: 检查玩家背包 description: 检查玩家背包
usage: /inspect <player> usage: /inspect <player>
home:
description: 回家
usage: /home [名称]
homes:
description: 查看家列表
usage: /homes
sethome:
description: 设置家
usage: /sethome [名称]
delhome:
description: 删除家
usage: /delhome <名称>