Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
cd9f4549b2 | |||
6937a15225 | |||
937f9fcd8f | |||
06aeb33f50 | |||
6381d51950 | |||
b44e5a658b | |||
6a7e8b066a | |||
43dc8f0abc | |||
2da5d164d7 | |||
b71fec6329 | |||
50c8d8812a | |||
ad8fc39ab8 | |||
37b773c6c3 | |||
e50f0d035c | |||
cbf9718fb0 | |||
f19d8de23d | |||
440c4c9fa6 | |||
cb9716e7b0 | |||
af7223c27f | |||
aa8dc8aecf | |||
6354d04755 | |||
79fb780745 | |||
ff05bb0317 | |||
d854a9b1ef | |||
e051167757 | |||
87d0436821 | |||
6facb9d2db | |||
5def80739c | |||
a9406be53c | |||
2d10ead566 |
@ -15,10 +15,10 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: "Set up Maven"
|
||||
uses: https://ssl.lunadeer.cn:14446/actions/setup-maven@v4
|
||||
- name: "Set up JDK 17"
|
||||
- name: "Set up JDK 21"
|
||||
uses: https://ssl.lunadeer.cn:14446/actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
distribution: 'zulu'
|
||||
cache: maven
|
||||
- name: "Build with Maven"
|
||||
|
186
README.md
186
README.md
@ -7,7 +7,7 @@ EssentialsX,其中D代表Deer。
|
||||
|
||||
## 支持版本
|
||||
|
||||
- 1.20.1+ (Folia)
|
||||
- 1.21.1+ (Folia)
|
||||
|
||||
## 安装方法
|
||||
|
||||
@ -16,188 +16,4 @@ EssentialsX,其中D代表Deer。
|
||||
3. 在 `plugins/EssentialsD/config.yml` 中配置
|
||||
4. 重启服务器
|
||||
|
||||
## 玩家使用方法
|
||||
|
||||
### 1. 隐形(发光)展示框
|
||||
|
||||
![image-20240118174003155](https://ssl.lunadeer.cn:14437/i/2024/01/18/65a8f1f380640.png)
|
||||
|
||||
![image-20240118174020596](https://ssl.lunadeer.cn:14437/i/2024/01/18/65a8f204dcb15.png)
|
||||
|
||||
可以通过`八个木棍`+`一个玻璃板`合成隐形展示框![](https://ssl.lunadeer.cn:14437/i/2024/04/19/6621ce4eeabbf.png)
|
||||
|
||||
或者`一个隐形展示框`+`一个萤石粉`合成隐形发光展示框![](https://ssl.lunadeer.cn:14437/i/2024/04/19/6621ce915ef3c.png)
|
||||
|
||||
当隐形展示框中没有东西时和普通展示框没有区别,**只有在将要展示的物品放置在隐形展示框中时展示框才会自动隐形**
|
||||
。取下展示框中的物品后展示框会自动恢复到现形状态,避免找不到隐形的空展示框。
|
||||
|
||||
### 2. 带手的盔甲架
|
||||
|
||||
使用 `木棍` 对着普通盔甲架单击 `鼠标左键`,即可给盔甲架装上手臂。
|
||||
|
||||
![](https://ssl.lunadeer.cn:14437/i/2024/01/16/65a62d9d94d8f.gif)
|
||||
|
||||
> 由于游戏特性,盔甲架被打掉后手臂会自动消失,因此每次防止盔甲架都需要重新安装手臂。
|
||||
|
||||
### 3. 自杀指令
|
||||
|
||||
使用 `/suicide` 即可自杀,避免因为特殊情况被卡在门中或误入禁区无法动弹。
|
||||
|
||||
### 4. 帽子
|
||||
|
||||
使用 `/hat` 可将主手持(一般为右手)的任意物品放到头上,如果头上有东西
|
||||
|
||||
### 5. 展示物品
|
||||
|
||||
使用 `/showitem` 可以向服务器内的所有玩家展示你主手持(一般为右手)的物品。
|
||||
|
||||
### 6. 头颅
|
||||
|
||||
使用 `/skull` 可将背包中的一个头颅交换为自己的头颅,注意:需要你的背包中有任意一个除玩家之外生物的头颅(苦力怕、凋零、小白等都可以)。
|
||||
|
||||
### 7. 撬棍
|
||||
|
||||
![image-20240118174110521](https://ssl.lunadeer.cn:14437/i/2024/01/18/65a8f236d8aa6.png)
|
||||
|
||||
使用`一个铁锭`+`一个木棍`
|
||||
合成撬棍![image-20240118173808266](https://ssl.lunadeer.cn:14437/i/2024/01/18/65a8f1815937f.png)
|
||||
|
||||
使用撬棍右击平放的铁轨可以改变铁轨的方向。
|
||||
|
||||
### 8. 快速末影箱
|
||||
|
||||
使用 `enderchest` 可以快速打开末影箱。
|
||||
|
||||
### 9. 附魔书堆叠
|
||||
|
||||
使用两本书可以合成堆叠为2的附魔书:
|
||||
|
||||
![](https://ssl.lunadeer.cn:14437/i/2024/03/19/65f86b5f25303.png)
|
||||
|
||||
此种特殊堆叠可以用于实现大吸力漏斗等特殊效果(详情可自行搜索学习)。
|
||||
|
||||
### 10. 光源方块
|
||||
|
||||
可以使用一个火把转换成一个光源方块,光源方块可用于建筑美化。
|
||||
|
||||
![](https://ssl.lunadeer.cn:14437/i/2024/03/23/65fed55d7815a.png)
|
||||
|
||||
>
|
||||
以下摘自WIKI:作为一种被设计为隐藏自身存在的方块,光源方块有许多与结构空位相似的性质。光源方块通常是不可见的,并且没有碰撞箱。与结构空位类似,光源方块不会被水或熔岩冲毁,能够阻止其通过。光源方块不能在非创造模式中被挖掘破坏。光源方块会阻碍植物生长。活塞不能推动光源方块。光源方块具有3,600,000.8爆炸抗性,并阻止游戏中的常见爆炸透过光源方块破坏方块。光源方块具有一个完整方块大小的判定箱,但只在玩家手持光源方块时才可以选中,否则玩家只能选中其后方的物体。在光源方块的判定箱上尝试放置不是光源方块的方块时,方块会取代光源方块。光源方块可以含水。含水的光影方块可以通过在光源方块中放置水或在水中放置光源方块来获得,允许玩家创造发光且不会结冰或被熔岩破坏的水体。光源方块是一种“隐形方块”,处于开启了“显示隐形方块”的储存模式结构方块的存储区域范围内的光源方块会显示出一个淡黄色的立方体框。
|
||||
|
||||
### 11. 传送
|
||||
|
||||
使用 `/tpa <player>` 可以向指定玩家发送传送请求,对方可以选择接受或拒绝。
|
||||
|
||||
### 12. 椅子
|
||||
|
||||
你可以将楼梯当作椅子右键坐下(根据服主配置可能需要在楼梯两边放置木板)。
|
||||
|
||||
![](https://ssl.lunadeer.cn:14437/i/2024/04/17/661fd53068264.png)
|
||||
|
||||
## 管理员指南
|
||||
|
||||
### 1. 无敌模式
|
||||
|
||||
使用 `/god [player]` 可以将自己或其他玩家设置为无敌模式,无敌模式下玩家不会受到任何伤害。
|
||||
|
||||
### 2. 飞行模式
|
||||
|
||||
使用 `/fly [player]` 可以将自己或其他玩家设置为飞行模式,飞行模式下玩家可以在空中飞行。
|
||||
|
||||
### 3. 复制手持物品
|
||||
|
||||
使用 `/more [amount]` 可以获取更多手持物品,如果不填写数量则默认为64个。
|
||||
|
||||
### 4. 设置世界时间
|
||||
|
||||
- 使用 `/day` 可以将世界时间设置为白天。
|
||||
- 使用 `/noon` 可以将世界时间设置为正午。
|
||||
- 使用 `/night` 可以将世界时间设置为黑夜。
|
||||
|
||||
### 5. 设置天气
|
||||
|
||||
- 使用 `/sun` 可以将天气设置为晴天。
|
||||
- 使用 `/rain` 可以将天气设置为下雨。
|
||||
- 使用 `/storm` 可以将天气设置为雷雨。
|
||||
|
||||
### 6. 区域强加载
|
||||
|
||||
在 `plugins/EssentialsD/config.yml` 中的 `ForceLoadChunks` 中添加需要强加载的区块,格式为 `world:x:z`,其中 `world`
|
||||
为世界名称,`x` 和 `z` 为区块坐标。
|
||||
|
||||
### 7. 查看玩家背包
|
||||
|
||||
使用 `/inspect` 命令可以查看玩家的背包。
|
||||
|
||||
```yaml
|
||||
ForceLoadChunks:
|
||||
- world:0:0
|
||||
- world_the_end:-12:12
|
||||
```
|
||||
|
||||
## 指令
|
||||
|
||||
以下指令尖括号`<>`表示必填参数,方括号`[]`表示可选参数。
|
||||
|
||||
### 玩家指令
|
||||
|
||||
| 指令 | 说明 |
|
||||
|-----------------|-------------------|
|
||||
| `/suicide` | 自杀 |
|
||||
| `/hat` | 将主手持的物品放在头部 |
|
||||
| `/showitem` | 展示主手持的物品 |
|
||||
| `/skull` | 使用任意头颅交换获得一个自己的头颅 |
|
||||
| `/enderchest` | 通过指令打开末影箱 |
|
||||
| `/tpa <player>` | 请求传送到指定玩家位置 |
|
||||
|
||||
### 管理员指令
|
||||
|
||||
| 指令 | 说明 |
|
||||
|------------------|----------|
|
||||
| `/save` | 手动保存存档 |
|
||||
| `/god [player]` | 无敌模式 |
|
||||
| `/fly [player]` | 飞行模式 |
|
||||
| `/more [amount]` | 获取更多手持物品 |
|
||||
| `/day` | 设置白天 |
|
||||
| `/noon` | 设置正午 |
|
||||
| `/night` | 设置黑夜 |
|
||||
| `/sun` | 设置晴天 |
|
||||
| `/rain` | 设置下雨 |
|
||||
| `/storm` | 设置雷雨 |
|
||||
|
||||
## 配置文件参考
|
||||
|
||||
```yaml
|
||||
# 附魔瓶经验值倍率
|
||||
ExpBottleRatio: 1.0
|
||||
|
||||
# 强加载区块列表
|
||||
ForceLoadChunks:
|
||||
# - world:0:0
|
||||
# - world_the_end:-12:12
|
||||
|
||||
# 区块操作延迟(强加载清理、设置) 秒
|
||||
# 过低可能导致世界未加载完成时触发操作引起报错
|
||||
# -1 为禁用强加载区块操作
|
||||
ChunkOperateDelay: 10
|
||||
|
||||
Teleport:
|
||||
Enable: true
|
||||
# 请求有效期 秒
|
||||
Expire: 30
|
||||
# 传送延迟 秒
|
||||
Delay: 0
|
||||
# 冷却时间 秒
|
||||
CoolDown: 0
|
||||
|
||||
Chair:
|
||||
Enable: true
|
||||
MaxWidth: 4
|
||||
SignCheck: false
|
||||
SitHeight: -0.95
|
||||
|
||||
Debug: false
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
47
pom.xml
47
pom.xml
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>cn.lunadeer</groupId>
|
||||
<artifactId>EssentialsD</artifactId>
|
||||
<version>1.18.13</version>
|
||||
<version>2.3.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>EssentialsD</name>
|
||||
@ -62,14 +62,43 @@
|
||||
<id>papermc</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>lunadeer-repo</id>
|
||||
<url>https://ssl.lunadeer.cn:14454/repository/maven-snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>placeholderapi</id>
|
||||
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.folia</groupId>
|
||||
<artifactId>folia-api</artifactId>
|
||||
<version>1.20.1-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.lunadeer</groupId>
|
||||
<artifactId>MinecraftPluginUtils</artifactId>
|
||||
<version>1.3.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.MilkBowl</groupId>
|
||||
<artifactId>VaultAPI</artifactId>
|
||||
<version>1.7</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.11.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -1,8 +1,31 @@
|
||||
package cn.lunadeer.essentialsd;
|
||||
|
||||
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.commands.time.Day;
|
||||
import cn.lunadeer.essentialsd.commands.time.Night;
|
||||
import cn.lunadeer.essentialsd.commands.time.Noon;
|
||||
import cn.lunadeer.essentialsd.commands.tp.Back;
|
||||
import cn.lunadeer.essentialsd.commands.tp.Rtp;
|
||||
import cn.lunadeer.essentialsd.commands.tp.Tpa;
|
||||
import cn.lunadeer.essentialsd.commands.tp.TpaHere;
|
||||
import cn.lunadeer.essentialsd.commands.warp.DelWarp;
|
||||
import cn.lunadeer.essentialsd.commands.warp.SetWarp;
|
||||
import cn.lunadeer.essentialsd.commands.warp.Warp;
|
||||
import cn.lunadeer.essentialsd.commands.warp.Warps;
|
||||
import cn.lunadeer.essentialsd.commands.weather.Rain;
|
||||
import cn.lunadeer.essentialsd.commands.weather.Storm;
|
||||
import cn.lunadeer.essentialsd.commands.weather.Sun;
|
||||
import cn.lunadeer.essentialsd.events.*;
|
||||
import cn.lunadeer.essentialsd.managers.ConfigManager;
|
||||
import cn.lunadeer.essentialsd.managers.DatabaseTables;
|
||||
import cn.lunadeer.essentialsd.managers.TabListUpdater;
|
||||
import cn.lunadeer.essentialsd.managers.TeleportManager;
|
||||
import cn.lunadeer.essentialsd.recipes.*;
|
||||
import cn.lunadeer.essentialsd.utils.*;
|
||||
import cn.lunadeer.minecraftpluginutils.*;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@ -14,12 +37,39 @@ public final class EssentialsD extends JavaPlugin {
|
||||
public void onEnable() {
|
||||
// Plugin startup logic
|
||||
instance = this;
|
||||
new XLogger(instance);
|
||||
config = new ConfigManager(instance);
|
||||
scheduler = new Scheduler(this);
|
||||
new Notification(instance);
|
||||
database = new DatabaseManager(this,
|
||||
DatabaseManager.TYPE.valueOf(config.getDbType().toUpperCase()),
|
||||
config.getDbHost(),
|
||||
config.getDbPort(),
|
||||
config.getDbName(),
|
||||
config.getDbUser(),
|
||||
config.getDbPass());
|
||||
DatabaseTables.migrate();
|
||||
new Scheduler(this);
|
||||
tpManager = new TeleportManager();
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new Events(), this);
|
||||
if (config.getPrefixEnable()) {
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
Bukkit.getPluginManager().registerEvents(new ChatPrefixEvent(), this);
|
||||
new TabListUpdater(); // 更新 TabList
|
||||
} else {
|
||||
XLogger.warn("未找到 PlaceholderAPI 插件, 无法使用聊天前缀功能, 已自动关闭前缀功能");
|
||||
config.setPrefixEnable(false);
|
||||
}
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new InvisibleItemFrameEvent(), 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);
|
||||
Bukkit.getPluginManager().registerEvents(new Experience(), this);
|
||||
// op 指令
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("fly")).setExecutor(new Fly());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("god")).setExecutor(new God());
|
||||
@ -38,9 +88,21 @@ public final class EssentialsD extends JavaPlugin {
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("hat")).setExecutor(new Hat());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("showitem")).setExecutor(new ShowItem());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("skull")).setExecutor(new Skull());
|
||||
// tp
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("tpa")).setExecutor(new Tpa());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("tpahere")).setExecutor(new TpaHere());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("rtp")).setExecutor(new Rtp());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("back")).setExecutor(new Back());
|
||||
// home
|
||||
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());
|
||||
// warp
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("setwarp")).setExecutor(new SetWarp());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("delwarp")).setExecutor(new DelWarp());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("warp")).setExecutor(new Warp());
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("warps")).setExecutor(new Warps());
|
||||
|
||||
|
||||
if (config.getRecipesCrowbar()) {
|
||||
@ -57,7 +119,7 @@ public final class EssentialsD extends JavaPlugin {
|
||||
this.getServer().addRecipe(LightBlock.getRecipe());
|
||||
}
|
||||
|
||||
Metrics metrics = new Metrics(this, 21861);
|
||||
bStatsMetrics metrics = new bStatsMetrics(this, 21861);
|
||||
if (config.getCheckUpdate()) {
|
||||
releaseCheck = new GiteaReleaseCheck(this,
|
||||
"https://ssl.lunadeer.cn:14446",
|
||||
@ -86,7 +148,8 @@ public final class EssentialsD extends JavaPlugin {
|
||||
|
||||
public static EssentialsD instance;
|
||||
public static ConfigManager config;
|
||||
public static Scheduler scheduler;
|
||||
|
||||
public static TeleportManager tpManager;
|
||||
public static DatabaseManager database;
|
||||
private GiteaReleaseCheck releaseCheck;
|
||||
}
|
||||
|
@ -1,178 +0,0 @@
|
||||
package cn.lunadeer.essentialsd;
|
||||
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.essentialsd.utils.STUI.Button;
|
||||
import cn.lunadeer.essentialsd.utils.XLogger;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class TeleportManager {
|
||||
|
||||
private static final TextColor main_color = TextColor.color(0, 233, 255);
|
||||
|
||||
private final ConcurrentHashMap<UUID, TpTask> _tasks = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<UUID, LocalDateTime> _next_time_allow_tp = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<UUID, Location> _last_tp_location = new ConcurrentHashMap<>();
|
||||
|
||||
private static class TpTask {
|
||||
public Player initiator;
|
||||
public Player target;
|
||||
public UUID taskId;
|
||||
}
|
||||
|
||||
public void request(Player initiator, Player target) {
|
||||
if (initiator == target) {
|
||||
Notification.error(initiator, "不能传送到自己的位置");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target.isOnline()) {
|
||||
Notification.error(initiator, "玩家 " + target.getName() + " 不在线");
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime next_time = _next_time_allow_tp.get(initiator.getUniqueId());
|
||||
if (next_time != null) {
|
||||
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();
|
||||
task.initiator = initiator;
|
||||
task.target = target;
|
||||
task.taskId = UUID.randomUUID();
|
||||
_tasks.put(task.taskId, task);
|
||||
|
||||
Notification.info(initiator, "已向 " + target.getName() + " 发送传送请求");
|
||||
|
||||
TextComponent acceptBtn = Button.createGreen("接受", "/tpa accept " + task.taskId);
|
||||
TextComponent denyBtn = Button.createRed("拒绝", "/tpa deny " + task.taskId);
|
||||
Notification.info(target, Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color));
|
||||
Notification.info(target, Component.text("| 玩家 " + initiator.getName() + " 请求传送到你的位置", main_color));
|
||||
Notification.info(target, Component.text("| 此请求将在 " + EssentialsD.config.getTpTpaExpire() + " 秒后失效", main_color));
|
||||
Notification.info(target, Component.text("| ", main_color).append(acceptBtn).append(Component.text(" ", main_color)).append(denyBtn));
|
||||
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) -> {
|
||||
_tasks.remove(task.taskId);
|
||||
}, 20L * EssentialsD.config.getTpTpaExpire());
|
||||
}
|
||||
|
||||
public void deny(Player player, UUID taskId) {
|
||||
TpTask task = _tasks.get(taskId);
|
||||
if (task == null) {
|
||||
Notification.error(player, "传送请求不存在或已过期");
|
||||
return;
|
||||
}
|
||||
if (task.target != player) {
|
||||
Notification.error(player, "这不是你的传送请求");
|
||||
return;
|
||||
}
|
||||
_tasks.remove(taskId);
|
||||
if (task.initiator.isOnline()) {
|
||||
Notification.error(task.initiator, "玩家 " + player.getName() + " 拒绝了你的传送请求");
|
||||
}
|
||||
if (task.target.isOnline()) {
|
||||
Notification.error(player, "已拒绝 " + task.initiator.getName() + " 的传送请求");
|
||||
}
|
||||
}
|
||||
|
||||
public void accept(Player player, UUID taskId) {
|
||||
TpTask task = _tasks.get(taskId);
|
||||
if (task == null) {
|
||||
Notification.error(player, "传送请求不存在或已过期");
|
||||
return;
|
||||
}
|
||||
if (task.target != player) {
|
||||
Notification.error(player, "这不是你的传送请求");
|
||||
return;
|
||||
}
|
||||
_tasks.remove(taskId);
|
||||
|
||||
if (!task.initiator.isOnline() || !task.target.isOnline()) {
|
||||
return;
|
||||
}
|
||||
Notification.info(task.target, "已接受 " + task.initiator.getName() + " 的传送请求");
|
||||
if (EssentialsD.config.getTpDelay() > 0) {
|
||||
Notification.info(task.initiator, "玩家 " + task.target.getName() + " 已接受你的传送请求,传送将在 " + EssentialsD.config.getTpDelay() + " 秒后进行");
|
||||
}
|
||||
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> {
|
||||
if (task.initiator.isOnline() && task.target.isOnline()) {
|
||||
doTeleportSafely(task.initiator, task.target.getLocation());
|
||||
Notification.info(task.initiator, "已传送到 " + task.target.getName() + " 的位置");
|
||||
Notification.info(task.target, "玩家 " + task.initiator.getName() + " 已传送到你的位置");
|
||||
}
|
||||
}, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay());
|
||||
}
|
||||
|
||||
public void back(Player player) {
|
||||
if (!_last_tp_location.containsKey(player.getUniqueId())) {
|
||||
Notification.error(player, "没有找到可返回的位置");
|
||||
return;
|
||||
}
|
||||
if (EssentialsD.config.getTpDelay() > 0) {
|
||||
Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后返回上次传送的位置");
|
||||
}
|
||||
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> {
|
||||
doTeleportSafely(player, _last_tp_location.get(player.getUniqueId()));
|
||||
Notification.info(player, "已返回上次传送的位置");
|
||||
}, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay());
|
||||
}
|
||||
|
||||
public void rtp(Player player) {
|
||||
int radius = EssentialsD.config.getTpRtpRadius();
|
||||
World world = player.getWorld();
|
||||
int x = (int) (Math.random() * radius * 2) - radius;
|
||||
int z = (int) (Math.random() * radius * 2) - radius;
|
||||
XLogger.debug("RTP: " + x + " " + z);
|
||||
Location location = new Location(world, x, player.getY(), z);
|
||||
if (EssentialsD.config.getTpDelay() > 0) {
|
||||
Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后传送到随机位置");
|
||||
}
|
||||
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> {
|
||||
doTeleportSafely(player, location);
|
||||
Notification.info(player, "已传送到随机位置");
|
||||
}, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay());
|
||||
}
|
||||
|
||||
/**
|
||||
* 把玩家传送到指定位置 并更新上次传送的位置
|
||||
*
|
||||
* @param player 玩家
|
||||
* @param location 位置
|
||||
*/
|
||||
public void doTeleportSafely(Player player, Location location) {
|
||||
location.getWorld().getChunkAtAsyncUrgently(location).thenAccept((chunk) -> {
|
||||
updateLastTpLocation(player);
|
||||
for (double y = location.y(); y < 256; y++) {
|
||||
Location up1 = location.getBlock().getRelative(BlockFace.UP).getLocation();
|
||||
Location up2 = up1.getBlock().getRelative(BlockFace.UP).getLocation();
|
||||
if (up1.getBlock().isPassable() && up2.getBlock().isPassable()) {
|
||||
break;
|
||||
} else {
|
||||
location.setY(y + 1);
|
||||
}
|
||||
}
|
||||
player.teleportAsync(location, PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||
});
|
||||
}
|
||||
|
||||
public void updateLastTpLocation(Player player) {
|
||||
_last_tp_location.put(player.getUniqueId(), player.getLocation());
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.essentialsd.utils.XLogger;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@ -10,26 +11,6 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Apis {
|
||||
/**
|
||||
* 判断 CommandSender 是否为 OP 玩家
|
||||
*
|
||||
* @param sender CommandSender
|
||||
* @return 如果是 OP 玩家则返回 Player 对象,否则返回 null
|
||||
*/
|
||||
public static Player getIfOP(CommandSender sender) {
|
||||
if (sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
if (!player.isOp()) {
|
||||
XLogger.warn("玩家 " + player.getName() + " 被拒绝执行命令");
|
||||
Notification.warn(player, "你没有权限使用此命令");
|
||||
return null;
|
||||
}
|
||||
return player;
|
||||
}
|
||||
Notification.warn(sender, "只有玩家才能使用此命令");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取命令操作的玩家 如果没有指定玩家则返回命令发送者
|
||||
*
|
||||
@ -37,43 +18,35 @@ public class Apis {
|
||||
* @param args 命令参数
|
||||
* @return 如果指定了玩家则返回玩家对象,否则返回命令发送者
|
||||
*/
|
||||
public static Player opCommandGetPlayer(CommandSender sender, String[] args) {
|
||||
Player target;
|
||||
if (args.length == 1) {
|
||||
target = EssentialsD.instance.getServer().getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
Notification.warn(sender, "玩家 " + args[0] + " 不在线");
|
||||
public static Player getPlayerFromArg(CommandSender sender, String[] args, int pos) {
|
||||
if (args.length <= pos) {
|
||||
if (sender instanceof Player) {
|
||||
return (Player) sender;
|
||||
} else {
|
||||
Notification.error(sender, "以控制台身份执行时,必须指定玩家");
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (!(sender instanceof Player)) {
|
||||
Notification.warn(sender, "请指定要操作的玩家");
|
||||
return null;
|
||||
}
|
||||
target = (Player) sender;
|
||||
}
|
||||
Player target = EssentialsD.instance.getServer().getPlayer(args[pos]);
|
||||
if (target == null) {
|
||||
Notification.warn(sender, "玩家 %s 不在线", args[0]);
|
||||
return null;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为 OP 玩家或控制台
|
||||
*
|
||||
* @param sender 命令发送者
|
||||
* @return 如果是 OP 玩家或控制台则返回 true,否则返回 false
|
||||
*/
|
||||
public static boolean notOpOrConsole(CommandSender sender) {
|
||||
if (sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
if (!player.isOp()) {
|
||||
XLogger.warn("玩家 " + player.getName() + " 被拒绝执行命令");
|
||||
Notification.warn(player, "你没有权限使用此命令");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<String> playerNames() {
|
||||
return EssentialsD.instance.getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static void setOverWorldTime(CommandSender sender, long time) {
|
||||
Scheduler.runTask(() -> {
|
||||
EssentialsD.instance.getServer().getWorlds().forEach(world -> {
|
||||
if (world.getEnvironment() == World.Environment.NORMAL) {
|
||||
world.setTime(time);
|
||||
Notification.info(sender, "设置 %s 时间为 %d", world.getName(), time);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -18,10 +18,6 @@ public class EnderChest implements CommandExecutor {
|
||||
Notification.error(sender, "只有玩家才能使用这个命令");
|
||||
return false;
|
||||
}
|
||||
if (!EssentialsD.config.getCommandsEnderchest()) {
|
||||
Notification.error(sender, "这个命令已被管理员禁用");
|
||||
return false;
|
||||
}
|
||||
// 打开玩家的末影箱
|
||||
Player player = (Player) sender;
|
||||
Inventory chest = player.getEnderChest();
|
||||
|
@ -1,34 +1,34 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.notOpOrConsole;
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.opCommandGetPlayer;
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.getPlayerFromArg;
|
||||
|
||||
public class Fly implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (notOpOrConsole(sender)) {
|
||||
if (!(sender instanceof Player) && args.length < 1) {
|
||||
Notification.error(sender, "以控制台身份执行时,必须指定玩家:/fly <player>");
|
||||
return false;
|
||||
}
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
Player target = opCommandGetPlayer(sender, args);
|
||||
Scheduler.runTask(() -> {
|
||||
Player target = getPlayerFromArg(sender, args, 0);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
if (target.getAllowFlight()) {
|
||||
target.setAllowFlight(false);
|
||||
Notification.info(sender, "已关闭玩家 " + target.getName() + " 的飞行模式");
|
||||
Notification.info(sender, "已关闭玩家 %s 的飞行模式", target.getName());
|
||||
Notification.info(target, "已关闭飞行模式");
|
||||
} else {
|
||||
target.setAllowFlight(true);
|
||||
Notification.info(sender, "已开启玩家 " + target.getName() + " 的飞行模式");
|
||||
Notification.info(sender, "已开启玩家 %s 的飞行模式", target.getName());
|
||||
Notification.info(target, "已开启飞行模式");
|
||||
}
|
||||
});
|
||||
|
@ -1,34 +1,34 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.notOpOrConsole;
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.opCommandGetPlayer;
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.getPlayerFromArg;
|
||||
|
||||
public class God implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (notOpOrConsole(sender)) {
|
||||
if (!(sender instanceof Player) && args.length < 1) {
|
||||
Notification.error(sender, "以控制台身份执行时,必须指定玩家:/god <player>");
|
||||
return false;
|
||||
}
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
Player target = opCommandGetPlayer(sender, args);
|
||||
Scheduler.runTask(() -> {
|
||||
Player target = getPlayerFromArg(sender, args, 0);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
if (target.isInvulnerable()) {
|
||||
target.setInvulnerable(false);
|
||||
Notification.info(sender, "已关闭玩家 " + target.getName() + " 的无敌模式");
|
||||
Notification.info(sender, "已关闭玩家 %s 的无敌模式", target.getName());
|
||||
Notification.info(target, "已关闭无敌模式");
|
||||
} else {
|
||||
target.setInvulnerable(true);
|
||||
Notification.info(sender, "已开启玩家 " + target.getName() + " 的无敌模式");
|
||||
Notification.info(sender, "已开启玩家 %s 的无敌模式", target.getName());
|
||||
Notification.info(target, "已开启无敌模式");
|
||||
}
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -17,10 +16,6 @@ public class Hat implements CommandExecutor {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
if (!EssentialsD.config.getCommandsHat()) {
|
||||
Notification.error(sender, "这个命令已被管理员禁用");
|
||||
return false;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
ItemStack right_hand_item = inventory.getItemInMainHand();
|
||||
|
@ -1,8 +1,7 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.essentialsd.utils.XLogger;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -10,7 +9,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.opCommandGetPlayer;
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.getPlayerFromArg;
|
||||
|
||||
public class Inspect implements CommandExecutor {
|
||||
/**
|
||||
@ -32,13 +31,8 @@ public class Inspect implements CommandExecutor {
|
||||
return false;
|
||||
}
|
||||
Player op = (Player) sender;
|
||||
if (!op.isOp()) {
|
||||
XLogger.warn("玩家 " + op.getName() + " 试图使用检查命令");
|
||||
Notification.error(op, "你没有权限使用这个命令");
|
||||
return false;
|
||||
}
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
Player target = opCommandGetPlayer(sender, args);
|
||||
Scheduler.runTask(() -> {
|
||||
Player target = getPlayerFromArg(op, args, 0);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -10,16 +10,15 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.getIfOP;
|
||||
|
||||
public class More implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
Player player = getIfOP(sender);
|
||||
if (player == null) {
|
||||
return false;
|
||||
if (!(sender instanceof Player)) {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
int amount = 64;
|
||||
Player player = (Player) sender;
|
||||
int amount = 63;
|
||||
if (args.length > 0) {
|
||||
try {
|
||||
amount = Integer.parseInt(args[0]);
|
||||
@ -41,7 +40,7 @@ public class More implements CommandExecutor {
|
||||
}
|
||||
}
|
||||
if (failed_add_count != 0) {
|
||||
Notification.warn(sender, "背包已满,有 " + failed_add_count + " 个物品未能添加");
|
||||
Notification.warn(sender, "背包已满,有 %d 个物品未能添加", failed_add_count);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.getIfOP;
|
||||
|
||||
public class Rain implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
Player player = getIfOP(sender);
|
||||
if (player == null) {
|
||||
return true;
|
||||
}
|
||||
// 设置天气为雨天
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
player.getWorld().setStorm(true);
|
||||
player.getWorld().setThundering(false);
|
||||
Notification.info(player, "设置 " + player.getWorld().getName() + " 天气为雨天");
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.notOpOrConsole;
|
||||
|
||||
public class Save implements CommandExecutor {
|
||||
/**
|
||||
@ -25,9 +24,6 @@ public class Save implements CommandExecutor {
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (notOpOrConsole(sender)) {
|
||||
return false;
|
||||
}
|
||||
// 保存服务器存档
|
||||
Notification.info(sender, "正在保存服务器存档...");
|
||||
EssentialsD.instance.getServer().savePlayers();
|
||||
|
@ -1,10 +1,10 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.stui.components.Button;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@ -27,10 +27,6 @@ public class ShowItem implements CommandExecutor {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
if (!EssentialsD.config.getCommandsShowItem()) {
|
||||
Notification.error(sender, "这个命令已被管理员禁用");
|
||||
return false;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
if (args.length == 1) {
|
||||
openView(player, args[0]);
|
||||
@ -42,23 +38,21 @@ public class ShowItem implements CommandExecutor {
|
||||
Notification.warn(player, "你的主手为空");
|
||||
return true;
|
||||
}
|
||||
Component name = item.displayName();
|
||||
String name_str = item.getType().name();
|
||||
if (item.getItemMeta().hasDisplayName()) {
|
||||
name_str = ((TextComponent) item.getItemMeta().displayName()).content();
|
||||
}
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
|
||||
TextComponent name_builder = Component.text()
|
||||
.append(Component.text("[", show_color))
|
||||
.append(name)
|
||||
.append(Component.text("]", show_color)).build();
|
||||
Component show = name_builder.hoverEvent(Component.text("点击查看物品信息"))
|
||||
.clickEvent(ClickEvent.runCommand("/showitem " + uuid));
|
||||
Button show = Button.create(name_str).setExecuteCommand("/showitem " + uuid).setHoverText("点击查看物品信息");
|
||||
|
||||
TextComponent title = Component.text(uuid.toString());
|
||||
TextComponent title = Component.text("物品展示").hoverEvent(Component.text(uuid.toString())).color(show_color);
|
||||
Inventory inv = EssentialsD.instance.getServer().createInventory(null, 54, title);
|
||||
inv.setItem(22, item);
|
||||
cache.put(uuid.toString(), inv);
|
||||
|
||||
player.getServer().sendMessage(Component.text("玩家 " + player.getName() + " 展示了物品 ").append(show));
|
||||
player.getServer().sendMessage(Component.text("玩家 " + player.getName() + " 展示了物品 ").append(show.build()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@ -23,10 +22,6 @@ public class Skull implements CommandExecutor {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
if (!EssentialsD.config.getCommandsSkull()) {
|
||||
Notification.error(sender, "这个命令已被管理员禁用");
|
||||
return false;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
PlayerInventory backpack = player.getInventory();
|
||||
Map<Integer, ? extends ItemStack> creeper_skulls = backpack.all(Material.CREEPER_HEAD);
|
||||
|
@ -1,28 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.getIfOP;
|
||||
|
||||
public class Storm implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
Player player = getIfOP(sender);
|
||||
if (player == null) {
|
||||
return true;
|
||||
}
|
||||
// 设置天气为雷雨
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
player.getWorld().setStorm(true);
|
||||
player.getWorld().setThundering(true);
|
||||
Notification.info(player, "设置 " + player.getWorld().getName() + " 天气为雷雨");
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -16,10 +15,6 @@ public class Suicide implements CommandExecutor {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
if (!EssentialsD.config.getCommandsSuicide()) {
|
||||
Notification.error(sender, "这个命令已被管理员禁用");
|
||||
return false;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
player.setHealth(0.0);
|
||||
player.setKiller(player);
|
||||
|
@ -1,28 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.getIfOP;
|
||||
|
||||
public class Sun implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
Player player = getIfOP(sender);
|
||||
if (player == null) {
|
||||
return true;
|
||||
}
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
// 设置天气为晴朗
|
||||
player.getWorld().setStorm(false);
|
||||
player.getWorld().setThundering(false);
|
||||
Notification.info(player, "设置 " + player.getWorld().getName() + " 天气为晴朗");
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package cn.lunadeer.essentialsd.commands.home;
|
||||
|
||||
import cn.lunadeer.essentialsd.dtos.HomeInfo;
|
||||
import cn.lunadeer.essentialsd.tuis.HomeList;
|
||||
import cn.lunadeer.minecraftpluginutils.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;
|
||||
}
|
||||
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, "成功删除家 %s", homeName);
|
||||
} else {
|
||||
Notification.error(player, "删除家 %s 失败, 请联系管理员", 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package cn.lunadeer.essentialsd.commands.home;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.dtos.HomeInfo;
|
||||
import cn.lunadeer.minecraftpluginutils.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;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
List<HomeInfo> homes = HomeInfo.getHomesOf(player.getUniqueId());
|
||||
String homeName;
|
||||
if (args.length == 0) {
|
||||
if (homes.size() == 0) {
|
||||
Notification.error(player, "你还没有设置家");
|
||||
return true;
|
||||
}
|
||||
homeName = homes.get(0).homeName;
|
||||
} else {
|
||||
homeName = args[0];
|
||||
}
|
||||
HomeInfo home = HomeInfo.getHome(player.getUniqueId(), homeName);
|
||||
if (home == null) {
|
||||
Notification.error(player, "不存在名为 %s 的家", homeName);
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
EssentialsD.tpManager.doTeleportDelayed(player, home.location, EssentialsD.config.getTpDelay(), () -> {
|
||||
Notification.info(player, "正在传送到家 %s", homeName);
|
||||
}, () -> {
|
||||
Notification.info(player, "成功传送到家 %s", homeName);
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
Notification.error(player, "传送到家 %s 失败: %s", 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.lunadeer.essentialsd.commands.home;
|
||||
|
||||
import cn.lunadeer.essentialsd.tuis.HomeList;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package cn.lunadeer.essentialsd.commands.home;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.dtos.HomeInfo;
|
||||
import cn.lunadeer.minecraftpluginutils.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.Collections;
|
||||
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;
|
||||
}
|
||||
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, "已经存在名为 %s 的家", info.homeName);
|
||||
return true;
|
||||
}
|
||||
boolean res = HomeInfo.newHome(info);
|
||||
if (res) {
|
||||
Notification.info(player, "成功设置家 %s", info.homeName);
|
||||
} else {
|
||||
Notification.error(player, "设置家 %s 失败, 请联系管理员", info.homeName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Collections.singletonList("[home名称]");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
package cn.lunadeer.essentialsd.commands.time;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.notOpOrConsole;
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.setOverWorldTime;
|
||||
|
||||
|
||||
public class Day implements CommandExecutor {
|
||||
/**
|
||||
@ -25,19 +23,8 @@ public class Day implements CommandExecutor {
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (notOpOrConsole(sender)) {
|
||||
return false;
|
||||
}
|
||||
// set time to 1000
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
if (sender instanceof Player) {
|
||||
((Player) sender).getWorld().setTime(1000);
|
||||
Notification.info(sender, "设置 " + ((Player) sender).getWorld().getName() + " 时间为白天");
|
||||
} else {
|
||||
EssentialsD.instance.getServer().getWorlds().forEach(world -> world.setTime(1000));
|
||||
Notification.info(sender, "设置时间为白天");
|
||||
}
|
||||
});
|
||||
setOverWorldTime(sender, 1000);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,14 +1,11 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
package cn.lunadeer.essentialsd.commands.time;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.notOpOrConsole;
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.setOverWorldTime;
|
||||
|
||||
public class Night implements CommandExecutor {
|
||||
/**
|
||||
@ -25,19 +22,7 @@ public class Night implements CommandExecutor {
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (notOpOrConsole(sender)) {
|
||||
return false;
|
||||
}
|
||||
// set time to 13000
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
if (sender instanceof Player) {
|
||||
((Player) sender).getWorld().setTime(13000);
|
||||
Notification.info(sender, "设置 " + ((Player) sender).getWorld().getName() + " 时间为夜晚");
|
||||
} else {
|
||||
EssentialsD.instance.getServer().getWorlds().forEach(world -> world.setTime(13000));
|
||||
Notification.info(sender, "设置时间为夜晚");
|
||||
}
|
||||
});
|
||||
setOverWorldTime(sender, 13000);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
package cn.lunadeer.essentialsd.commands.time;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.notOpOrConsole;
|
||||
import static cn.lunadeer.essentialsd.commands.Apis.setOverWorldTime;
|
||||
|
||||
|
||||
public class Noon implements CommandExecutor {
|
||||
/**
|
||||
@ -25,19 +23,7 @@ public class Noon implements CommandExecutor {
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (notOpOrConsole(sender)) {
|
||||
return false;
|
||||
}
|
||||
// set time to 6000
|
||||
EssentialsD.scheduler.global.run(EssentialsD.instance, (instance) -> {
|
||||
if (sender instanceof Player) {
|
||||
((Player) sender).getWorld().setTime(6000);
|
||||
Notification.info(sender, "设置 " + ((Player) sender).getWorld().getName() + " 时间为正午");
|
||||
} else {
|
||||
EssentialsD.instance.getServer().getWorlds().forEach(world -> world.setTime(6000));
|
||||
Notification.info(sender, "设置时间为正午");
|
||||
}
|
||||
});
|
||||
setOverWorldTime(sender, 6000);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
package cn.lunadeer.essentialsd.commands.tp;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.TeleportManager;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
@ -19,10 +18,6 @@ public class Back implements TabExecutor {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
if (!EssentialsD.config.getCommandsBack()) {
|
||||
Notification.error(sender, "这个命令已被管理员禁用");
|
||||
return false;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
EssentialsD.tpManager.back(player);
|
||||
return true;
|
@ -1,7 +1,7 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
package cn.lunadeer.essentialsd.commands.tp;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
@ -18,10 +18,6 @@ public class Rtp implements TabExecutor {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
if (!EssentialsD.config.getCommandsRtp()) {
|
||||
Notification.error(sender, "这个命令已被管理员禁用");
|
||||
return false;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
EssentialsD.tpManager.rtp(player);
|
||||
return true;
|
@ -1,7 +1,7 @@
|
||||
package cn.lunadeer.essentialsd.commands;
|
||||
package cn.lunadeer.essentialsd.commands.tp;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
@ -20,18 +20,14 @@ public class Tpa implements TabExecutor {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
if (!EssentialsD.config.getCommandsTpa()) {
|
||||
Notification.error(sender, "这个命令已被管理员禁用");
|
||||
return false;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
if (args.length == 1) {
|
||||
Player target = EssentialsD.instance.getServer().getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
Notification.warn(player, "玩家 " + args[0] + " 不在线");
|
||||
Notification.warn(player, "玩家 %s 不在线", args[0]);
|
||||
return true;
|
||||
}
|
||||
EssentialsD.tpManager.request(player, target);
|
||||
EssentialsD.tpManager.tpaRequest(player, target);
|
||||
return true;
|
||||
} else if (args.length == 2) {
|
||||
if (args[0].equals("accept")) {
|
@ -0,0 +1,49 @@
|
||||
package cn.lunadeer.essentialsd.commands.tp;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class TpaHere 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;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
if (args.length == 1) {
|
||||
Player target = EssentialsD.instance.getServer().getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
Notification.warn(player, "玩家 %s 不在线", args[0]);
|
||||
return true;
|
||||
}
|
||||
EssentialsD.tpManager.tpahereRequest(player, target);
|
||||
return true;
|
||||
} else {
|
||||
Notification.error(player, "参数错误");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
Collection<? extends Player> players = EssentialsD.instance.getServer().getOnlinePlayers();
|
||||
List<String> result = new java.util.ArrayList<>();
|
||||
for (Player player : players) {
|
||||
result.add(player.getName());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package cn.lunadeer.essentialsd.commands.warp;
|
||||
|
||||
import cn.lunadeer.essentialsd.dtos.WarpPoint;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DelWarp implements TabExecutor {
|
||||
|
||||
/**
|
||||
* /delwarp <name>
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
|
||||
if (strings.length != 1) {
|
||||
Notification.error(commandSender, "用法: /delwarp <name>");
|
||||
return true;
|
||||
}
|
||||
WarpPoint point = WarpPoint.selectByName(strings[0]);
|
||||
if (point == null) {
|
||||
Notification.error(commandSender, "传送点 %s 不存在", strings[0]);
|
||||
return true;
|
||||
}
|
||||
WarpPoint.delete(point);
|
||||
Notification.info(commandSender, "传送点 %s 已删除", strings[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
return WarpPoint.selectAllNames();
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package cn.lunadeer.essentialsd.commands.warp;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.dtos.WarpPoint;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
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 SetWarp implements TabExecutor {
|
||||
/**
|
||||
* /setwarp <name>
|
||||
* /setwarp <name> <world> <x> <y> <z>
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
|
||||
if (strings.length != 1 && strings.length != 5) {
|
||||
Notification.error(commandSender, "用法: /setwarp <name> 或 /setwarp <name> <world> <x> <y> <z>");
|
||||
return true;
|
||||
}
|
||||
String name = strings[0];
|
||||
Location location;
|
||||
if (strings.length == 1) {
|
||||
if (commandSender instanceof Player) {
|
||||
location = ((Player) commandSender).getLocation();
|
||||
} else {
|
||||
Notification.error(commandSender, "请指定坐标 /setwarp <name> <world> <x> <y> <z>");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
double x = Double.parseDouble(strings[1]);
|
||||
double y = Double.parseDouble(strings[2]);
|
||||
double z = Double.parseDouble(strings[3]);
|
||||
World world = EssentialsD.instance.getServer().getWorld(strings[0]);
|
||||
if (world == null) {
|
||||
Notification.error(commandSender, "世界 %s 不存在", strings[0]);
|
||||
return true;
|
||||
}
|
||||
location = new Location(world, x, y, z);
|
||||
} catch (NumberFormatException e) {
|
||||
Notification.error(commandSender, "坐标必须是数字");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
WarpPoint existing = WarpPoint.selectByName(name);
|
||||
if (existing != null) {
|
||||
Notification.error(commandSender, "传送点 %s 已存在", name);
|
||||
return true;
|
||||
}
|
||||
WarpPoint point = new WarpPoint(name, location);
|
||||
WarpPoint.insert(point);
|
||||
Notification.info(commandSender, "传送点 %s 已设置", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
List<String> res = new ArrayList<>();
|
||||
if (args.length == 1) {
|
||||
res.add("传送点名称");
|
||||
}
|
||||
if (args.length == 2) {
|
||||
res.add("世界名称");
|
||||
}
|
||||
if (args.length == 3) {
|
||||
res.add("x");
|
||||
}
|
||||
if (args.length == 4) {
|
||||
res.add("y");
|
||||
}
|
||||
if (args.length == 5) {
|
||||
res.add("z");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package cn.lunadeer.essentialsd.commands.warp;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.dtos.WarpPoint;
|
||||
import cn.lunadeer.minecraftpluginutils.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 Warp implements TabExecutor {
|
||||
|
||||
/**
|
||||
* /warp <name>
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
|
||||
if (!(commandSender instanceof Player)) {
|
||||
Notification.warn(commandSender, "只有玩家可以使用此命令");
|
||||
return true;
|
||||
}
|
||||
Player player = (Player) commandSender;
|
||||
if (strings.length != 1) {
|
||||
Notification.error(commandSender, "用法: /warp <name>");
|
||||
return true;
|
||||
}
|
||||
WarpPoint point = WarpPoint.selectByName(strings[0]);
|
||||
if (point == null) {
|
||||
Notification.error(commandSender, "传送点 %s 不存在", strings[0]);
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
EssentialsD.tpManager.doTeleportDelayed(player, point.getLocation(), EssentialsD.config.getTpDelay(), () -> {
|
||||
Notification.info(player, "正在传送到 %s", strings[0]);
|
||||
}, () -> {
|
||||
Notification.info(player, "已传送到 %s", strings[0]);
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
Notification.error(player, e.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
return WarpPoint.selectAllNames();
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package cn.lunadeer.essentialsd.commands.warp;
|
||||
|
||||
import cn.lunadeer.essentialsd.tuis.WarpList;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class Warps implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
|
||||
WarpList.show(commandSender, strings);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.lunadeer.essentialsd.commands.weather;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class Rain implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
Scheduler.runTask(() -> {
|
||||
EssentialsD.instance.getServer().getWorlds().forEach(world -> {
|
||||
if (world.getEnvironment() == World.Environment.NORMAL) {
|
||||
world.setStorm(true);
|
||||
world.setThundering(false);
|
||||
Notification.info(sender, "设置 %s 天气为雨天", world.getName());
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.lunadeer.essentialsd.commands.weather;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class Storm implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
Scheduler.runTask(() -> {
|
||||
EssentialsD.instance.getServer().getWorlds().forEach(world -> {
|
||||
if (world.getEnvironment() == World.Environment.NORMAL) {
|
||||
world.setStorm(true);
|
||||
world.setThundering(true);
|
||||
Notification.info(sender, "设置 %s 天气为雷雨", world.getName());
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.lunadeer.essentialsd.commands.weather;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class Sun implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
Scheduler.runTask(() -> {
|
||||
EssentialsD.instance.getServer().getWorlds().forEach(world -> {
|
||||
if (world.getEnvironment() == World.Environment.NORMAL) {
|
||||
world.setStorm(false);
|
||||
world.setThundering(false);
|
||||
Notification.info(sender, "设置 %s 天气为晴朗", world.getName());
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
83
src/main/java/cn/lunadeer/essentialsd/dtos/HomeInfo.java
Normal file
83
src/main/java/cn/lunadeer/essentialsd/dtos/HomeInfo.java
Normal file
@ -0,0 +1,83 @@
|
||||
package cn.lunadeer.essentialsd.dtos;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.LocationUtils;
|
||||
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 (?, ?, ?);";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, info.uuid, info.homeName, LocationUtils.Serialize(info.location))) {
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("创建Home失败", e, sql);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean deleteHome(UUID uuid, String homeName) {
|
||||
String sql = "DELETE FROM home_info WHERE uuid = ? AND home_name = ?;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, uuid.toString(), homeName)) {
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("删除Home失败", e, sql);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<HomeInfo> getHomesOf(UUID uuid) {
|
||||
List<HomeInfo> homes = new ArrayList<>();
|
||||
String sql = "SELECT * FROM home_info WHERE uuid = ?;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, uuid.toString())) {
|
||||
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) {
|
||||
EssentialsD.database.handleDatabaseError("获取Home列表失败", e, sql);
|
||||
}
|
||||
return homes;
|
||||
}
|
||||
|
||||
public static HomeInfo getHome(UUID uuid, String homeName) {
|
||||
String sql = "SELECT * FROM home_info WHERE uuid = ? AND home_name = ?;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, uuid.toString(), homeName)) {
|
||||
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) {
|
||||
EssentialsD.database.handleDatabaseError("获取Home失败", e, sql);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
47
src/main/java/cn/lunadeer/essentialsd/dtos/LoginRecord.java
Normal file
47
src/main/java/cn/lunadeer/essentialsd/dtos/LoginRecord.java
Normal file
@ -0,0 +1,47 @@
|
||||
package cn.lunadeer.essentialsd.dtos;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.LocationUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Timestamp;
|
||||
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();
|
||||
}
|
||||
Timestamp login_time = new Timestamp(player.getLastLogin());
|
||||
Timestamp logout_time = new Timestamp(System.currentTimeMillis());
|
||||
String logout_location = LocationUtils.Serialize(player.getLocation());
|
||||
String sql = "INSERT INTO login_record (uuid, ip, login_time, logout_location, logout_time) " +
|
||||
"VALUES (?, ?, ?, ?, ?);";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, uuid.toString(), ip, login_time, logout_location, logout_time)) {
|
||||
if (rs == null) return;
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("创建登录记录失败", e, sql);
|
||||
}
|
||||
}
|
||||
}
|
61
src/main/java/cn/lunadeer/essentialsd/dtos/NameRecord.java
Normal file
61
src/main/java/cn/lunadeer/essentialsd/dtos/NameRecord.java
Normal file
@ -0,0 +1,61 @@
|
||||
package cn.lunadeer.essentialsd.dtos;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
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 (?, ?, CURRENT_TIMESTAMP);";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, uuid.toString(), name)) {
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("创建名字记录失败", e, sql);
|
||||
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 = ? ORDER BY time DESC LIMIT " + limit + ";";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, uuid.toString())) {
|
||||
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) {
|
||||
EssentialsD.database.handleDatabaseError("获取名字记录失败", e, sql);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
}
|
42
src/main/java/cn/lunadeer/essentialsd/dtos/PlayerName.java
Normal file
42
src/main/java/cn/lunadeer/essentialsd/dtos/PlayerName.java
Normal file
@ -0,0 +1,42 @@
|
||||
package cn.lunadeer.essentialsd.dtos;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
|
||||
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 = ?;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, uuid.toString())) {
|
||||
if (rs == null) return null;
|
||||
if (rs.next()) {
|
||||
return rs.getString("last_known_name");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("获取玩家名字失败", e, sql);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean setName(UUID uuid, String name) {
|
||||
String sql = "INSERT INTO player_name (uuid, last_known_name) " +
|
||||
"VALUES (?, ?) ON CONFLICT (uuid) DO NOTHING;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, uuid.toString(), name)) {
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("设置玩家名字失败", e, sql);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean updateName(UUID uuid, String name) {
|
||||
String sql = "UPDATE player_name SET last_known_name = ? WHERE uuid = ?;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, name, uuid.toString())) {
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("更新玩家名字失败", e, sql);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
100
src/main/java/cn/lunadeer/essentialsd/dtos/WarpPoint.java
Normal file
100
src/main/java/cn/lunadeer/essentialsd/dtos/WarpPoint.java
Normal file
@ -0,0 +1,100 @@
|
||||
package cn.lunadeer.essentialsd.dtos;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.utils.LocUtil;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WarpPoint {
|
||||
private Integer id;
|
||||
private final String name;
|
||||
private final Location location;
|
||||
|
||||
public WarpPoint(Integer id, String name, Location location) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public WarpPoint(String name, Location location) {
|
||||
this.name = name;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public static void insert(WarpPoint point) {
|
||||
String sql = "INSERT INTO warp_point (warp_name, location) VALUES (?, ?);";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, point.getName(), LocUtil.toString(point.getLocation()))) {
|
||||
if (rs == null) return;
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("插入传送点失败", e, sql);
|
||||
}
|
||||
}
|
||||
|
||||
public static void delete(WarpPoint point) {
|
||||
String sql = "DELETE FROM warp_point WHERE warp_name = ?;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, point.getName())) {
|
||||
if (rs == null) return;
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("删除传送点失败", e, sql);
|
||||
}
|
||||
}
|
||||
|
||||
public static WarpPoint selectByName(String name) {
|
||||
String sql = "SELECT * FROM warp_point WHERE warp_name = ?;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql, name)) {
|
||||
if (rs == null) return null;
|
||||
if (rs.next()) {
|
||||
return new WarpPoint(rs.getInt("id"), rs.getString("warp_name"), LocUtil.fromString(rs.getString("location")));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("查询传送点失败", e, sql);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<String> selectAllNames() {
|
||||
List<String> names = new ArrayList<>();
|
||||
String sql = "SELECT DISTINCT warp_name FROM warp_point;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql)) {
|
||||
if (rs == null) return names;
|
||||
while (rs.next()) {
|
||||
names.add(rs.getString("warp_name"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("查询传送点名字失败", e, sql);
|
||||
return names;
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
public static List<WarpPoint> selectAll() {
|
||||
List<WarpPoint> points = new ArrayList<>();
|
||||
String sql = "SELECT * FROM warp_point;";
|
||||
try (ResultSet rs = EssentialsD.database.query(sql)) {
|
||||
if (rs == null) return points;
|
||||
while (rs.next()) {
|
||||
points.add(new WarpPoint(rs.getInt("id"), rs.getString("warp_name"), LocUtil.fromString(rs.getString("location"))));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
EssentialsD.database.handleDatabaseError("查询传送点失败", e, sql);
|
||||
return points;
|
||||
}
|
||||
return points;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package cn.lunadeer.essentialsd;
|
||||
package cn.lunadeer.essentialsd.events;
|
||||
|
||||
import cn.lunadeer.essentialsd.utils.XLogger;
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.XLogger;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
@ -15,11 +16,11 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.entity.EntityDismountEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.spigotmc.event.entity.EntityDismountEvent;
|
||||
|
||||
public class ChairEvent implements Listener {
|
||||
|
@ -0,0 +1,19 @@
|
||||
package cn.lunadeer.essentialsd.events;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
public class ChatPrefixEvent implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onChat(AsyncPlayerChatEvent event) {
|
||||
event.setCancelled(true);
|
||||
String formated = PlaceholderAPI.setPlaceholders(event.getPlayer(), EssentialsD.config.getPrefixChatFormat());
|
||||
formated += event.getMessage();
|
||||
EssentialsD.instance.getServer().broadcastMessage(formated);
|
||||
}
|
||||
|
||||
}
|
57
src/main/java/cn/lunadeer/essentialsd/events/CrowEvent.java
Normal file
57
src/main/java/cn/lunadeer/essentialsd/events/CrowEvent.java
Normal file
@ -0,0 +1,57 @@
|
||||
package cn.lunadeer.essentialsd.events;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.recipes.Crowbar;
|
||||
import cn.lunadeer.minecraftpluginutils.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);
|
||||
}
|
||||
}
|
@ -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()));
|
||||
}
|
||||
}
|
48
src/main/java/cn/lunadeer/essentialsd/events/Experience.java
Normal file
48
src/main/java/cn/lunadeer/essentialsd/events/Experience.java
Normal file
@ -0,0 +1,48 @@
|
||||
package cn.lunadeer.essentialsd.events;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.ExperienceOrb;
|
||||
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.EntitySpawnEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class Experience implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onExpOrbSpawn(EntitySpawnEvent event) {
|
||||
if (!EssentialsD.config.getCombineExpOrbs()) {
|
||||
return;
|
||||
}
|
||||
Entity entity = event.getEntity();
|
||||
if (entity.getType() != EntityType.EXPERIENCE_ORB) {
|
||||
return;
|
||||
}
|
||||
ExperienceOrb orb = (ExperienceOrb) entity;
|
||||
Location loc = entity.getLocation();
|
||||
double radius = EssentialsD.config.getCombineExpOrbsRadius();
|
||||
Collection<Entity> entities = loc.getNearbyEntities(radius, radius, radius);
|
||||
for (Entity e : entities) {
|
||||
if (e.getType() != EntityType.EXPERIENCE_ORB) {
|
||||
continue;
|
||||
}
|
||||
orb.setExperience(orb.getExperience() + ((ExperienceOrb) e).getExperience());
|
||||
e.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerLogin(PlayerLoginEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (EssentialsD.config.getNoExpCoolDown()) {
|
||||
player.setExpCooldown(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +1,23 @@
|
||||
package cn.lunadeer.essentialsd;
|
||||
package cn.lunadeer.essentialsd.events;
|
||||
|
||||
import cn.lunadeer.essentialsd.commands.ShowItem;
|
||||
import cn.lunadeer.essentialsd.recipes.Crowbar;
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.recipes.InvisibleGlowItemFrame;
|
||||
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.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.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
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.HangingPlaceEvent;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
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.persistence.PersistentDataType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Events 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);
|
||||
}
|
||||
|
||||
public class InvisibleItemFrameEvent implements Listener {
|
||||
// place an Invisible Item Frame
|
||||
@EventHandler
|
||||
public void placeItemFrame(HangingPlaceEvent event) {
|
||||
@ -127,80 +97,4 @@ public class Events implements Listener {
|
||||
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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package cn.lunadeer.essentialsd.events;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.commands.ShowItem;
|
||||
import cn.lunadeer.minecraftpluginutils.XLogger;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
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();
|
||||
Component title = view.title();
|
||||
HoverEvent<?> hoverEvent = title.hoverEvent();
|
||||
if (hoverEvent == null) {
|
||||
return;
|
||||
}
|
||||
if (!(hoverEvent.value() instanceof TextComponent)) {
|
||||
return;
|
||||
}
|
||||
TextComponent hoverEventValue = (TextComponent) hoverEvent.value();
|
||||
XLogger.debug("ShowItemEvent: " + hoverEventValue.content());
|
||||
if (!ShowItem.cache.containsKey(hoverEventValue.content())) {
|
||||
return;
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,390 @@
|
||||
package cn.lunadeer.essentialsd.managers;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import cn.lunadeer.minecraftpluginutils.XLogger;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigManager {
|
||||
public ConfigManager(EssentialsD plugin) {
|
||||
_plugin = plugin;
|
||||
_plugin.saveDefaultConfig();
|
||||
reload();
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
_plugin.reloadConfig();
|
||||
_file = _plugin.getConfig();
|
||||
_debug = _file.getBoolean("Debug", false);
|
||||
XLogger.setDebug(isDebug());
|
||||
_check_update = _file.getBoolean("CheckUpdate", true);
|
||||
_exp_bottle_ratio = (float) _file.getDouble("ExpBottleRatio", 1.0);
|
||||
_combine_exp_orbs_enable = _file.getBoolean("CombineExpOrbs.Enable", false);
|
||||
_combine_exp_orbs_radius = (float) _file.getDouble("CombineExpOrbs.Radius", 1.5);
|
||||
_no_exp_cool_down = _file.getBoolean("NoExpCoolDown", false);
|
||||
_force_load_chunks = _file.getStringList("ForceLoadChunks");
|
||||
_chunk_operate_delay = _file.getInt("ChunkOperateDelay", 10);
|
||||
_tp_delay = _file.getInt("Teleport.Delay", 0);
|
||||
_tp_cool_down = _file.getInt("Teleport.CoolDown", 0);
|
||||
_tp_tpa_expire = _file.getInt("Teleport.TpaExpire", 30);
|
||||
_tp_rtp_radius = _file.getInt("Teleport.RtpRadius", 1000);
|
||||
_tp_world_blacklist = _file.getStringList("Teleport.WorldBlackList");
|
||||
_chair_enable = _file.getBoolean("Chair.Enable", true);
|
||||
_chair_max_width = _file.getInt("Chair.MaxWidth", 4);
|
||||
_chair_sign_check = _file.getBoolean("Chair.SignCheck", true);
|
||||
_chair_sit_height = (float) _file.getDouble("Chair.SitHeight", -0.95);
|
||||
_recipes_crowbar = _file.getBoolean("Recipes.CrowBar", true);
|
||||
_recipes_invisible_item_frame = _file.getBoolean("Recipes.InvisibleItemFrame", true);
|
||||
_recipes_light_block = _file.getBoolean("Recipes.LightBlock", 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");
|
||||
if (!_db_type.equals("pgsql") && !_db_type.equals("sqlite")) {
|
||||
XLogger.err("当前数据库只支持 pgsql 或 sqlite,已重置为 sqlite");
|
||||
setDbType("sqlite");
|
||||
}
|
||||
_db_host = _file.getString("Database.Host", "localhost");
|
||||
_db_port = _file.getString("Database.Port", "5432");
|
||||
_db_name = _file.getString("Database.Name", "dominion");
|
||||
_db_user = _file.getString("Database.User", "postgres");
|
||||
_db_pass = _file.getString("Database.Pass", "postgres");
|
||||
_prefix_enable = _file.getBoolean("Prefix.Enable", true);
|
||||
_prefix_chat_format = _file.getString("Prefix.ChatFormat", "<%player_name%> ");
|
||||
_prefix_tab_format = _file.getString("Prefix.TabFormat", "%player_name%");
|
||||
saveAll(); // save all to make sure all values are valid
|
||||
}
|
||||
|
||||
private void saveAll() {
|
||||
_file.set("Debug", _debug);
|
||||
_file.set("CheckUpdate", _check_update);
|
||||
_file.set("ExpBottleRatio", _exp_bottle_ratio);
|
||||
_file.set("CombineExpOrbs.Enable", _combine_exp_orbs_enable);
|
||||
_file.set("CombineExpOrbs.Radius", _combine_exp_orbs_radius);
|
||||
_file.set("NoExpCoolDown", _no_exp_cool_down);
|
||||
_file.set("ForceLoadChunks", _force_load_chunks);
|
||||
_file.set("ChunkOperateDelay", _chunk_operate_delay);
|
||||
_file.set("Teleport.Delay", _tp_delay);
|
||||
_file.set("Teleport.CoolDown", _tp_cool_down);
|
||||
_file.set("Teleport.TpaExpire", _tp_tpa_expire);
|
||||
_file.set("Teleport.RtpRadius", _tp_rtp_radius);
|
||||
_file.set("Teleport.WorldBlackList", _tp_world_blacklist);
|
||||
_file.set("Chair.Enable", _chair_enable);
|
||||
_file.set("Chair.MaxWidth", _chair_max_width);
|
||||
_file.set("Chair.SignCheck", _chair_sign_check);
|
||||
_file.set("Chair.SitHeight", _chair_sit_height);
|
||||
_file.set("Recipes.CrowBar", _recipes_crowbar);
|
||||
_file.set("Recipes.InvisibleItemFrame", _recipes_invisible_item_frame);
|
||||
_file.set("Recipes.LightBlock", _recipes_light_block);
|
||||
_file.set("Recipes.StackedEnchantBook", _recipes_stacked_enchant_book);
|
||||
_file.set("HomeLimit.Amount", _home_limit_amount);
|
||||
_file.set("HomeLimit.WorldBlacklist", _home_world_blacklist);
|
||||
_file.set("Database.Type", _db_type);
|
||||
_file.set("Database.Host", _db_host);
|
||||
_file.set("Database.Port", _db_port);
|
||||
_file.set("Database.Name", _db_name);
|
||||
_file.set("Database.User", _db_user);
|
||||
_file.set("Database.Pass", _db_pass);
|
||||
_file.set("Prefix.Enable", _prefix_enable);
|
||||
_file.set("Prefix.ChatFormat", _prefix_chat_format);
|
||||
_file.set("Prefix.TabFormat", _prefix_tab_format);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Boolean isDebug() {
|
||||
return _debug;
|
||||
}
|
||||
|
||||
public void setDebug(Boolean debug) {
|
||||
_debug = debug;
|
||||
_file.set("Debug", debug);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Boolean getCheckUpdate() {
|
||||
return _check_update;
|
||||
}
|
||||
|
||||
public float getExpBottleRatio() {
|
||||
return _exp_bottle_ratio;
|
||||
}
|
||||
|
||||
public void setExpBottleRatio(float ratio) {
|
||||
_exp_bottle_ratio = ratio;
|
||||
_file.set("ExpBottleRatio", ratio);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Boolean getCombineExpOrbs() {
|
||||
return _combine_exp_orbs_enable;
|
||||
}
|
||||
|
||||
public void setCombineExpOrbs(Boolean combine) {
|
||||
_combine_exp_orbs_enable = combine;
|
||||
_file.set("CombineExpOrbs.Enable", combine);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Float getCombineExpOrbsRadius() {
|
||||
return _combine_exp_orbs_radius;
|
||||
}
|
||||
|
||||
public void setCombineExpOrbsRadius(Float radius) {
|
||||
_combine_exp_orbs_radius = radius;
|
||||
_file.set("CombineExpOrbs.Radius", radius);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Boolean getNoExpCoolDown() {
|
||||
return _no_exp_cool_down;
|
||||
}
|
||||
|
||||
public void setNoExpCoolDown(Boolean no_cool_down) {
|
||||
_no_exp_cool_down = no_cool_down;
|
||||
_file.set("NoExpCoolDown", no_cool_down);
|
||||
_plugin.saveConfig();
|
||||
for (Player player : EssentialsD.instance.getServer().getOnlinePlayers()) {
|
||||
if (EssentialsD.config.getNoExpCoolDown()) {
|
||||
player.setExpCooldown(0);
|
||||
} else {
|
||||
player.setExpCooldown(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getChunkOperateDelay() {
|
||||
return _chunk_operate_delay;
|
||||
}
|
||||
|
||||
public void setChunkOperateDelay(Integer delay) {
|
||||
_chunk_operate_delay = delay;
|
||||
_file.set("ChunkOperateDelay", delay);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Integer getTpDelay() {
|
||||
return _tp_delay;
|
||||
}
|
||||
|
||||
public Integer getTpCoolDown() {
|
||||
return _tp_cool_down;
|
||||
}
|
||||
|
||||
public Integer getTpTpaExpire() {
|
||||
return _tp_tpa_expire;
|
||||
}
|
||||
|
||||
public Integer getTpRtpRadius() {
|
||||
return _tp_rtp_radius;
|
||||
}
|
||||
|
||||
public List<String> getTpWorldBlackList() {
|
||||
return _tp_world_blacklist;
|
||||
}
|
||||
|
||||
public Boolean getChairEnable() {
|
||||
return _chair_enable;
|
||||
}
|
||||
|
||||
public Integer getChairMaxWidth() {
|
||||
return _chair_max_width;
|
||||
}
|
||||
|
||||
public Boolean getChairSignCheck() {
|
||||
return _chair_sign_check;
|
||||
}
|
||||
|
||||
public Float getChairSitHeight() {
|
||||
return _chair_sit_height;
|
||||
}
|
||||
|
||||
public Boolean getRecipesCrowbar() {
|
||||
return _recipes_crowbar;
|
||||
}
|
||||
|
||||
public Boolean getRecipesInvisibleItemFrame() {
|
||||
return _recipes_invisible_item_frame;
|
||||
}
|
||||
|
||||
public Boolean getRecipesLightBlock() {
|
||||
return _recipes_light_block;
|
||||
}
|
||||
|
||||
public Boolean getRecipesStackedEnchantBook() {
|
||||
return _recipes_stacked_enchant_book;
|
||||
}
|
||||
|
||||
public void ApplyForceLoadChunks() {
|
||||
if (_chunk_operate_delay < 0) {
|
||||
XLogger.info("加载区块操作已禁用");
|
||||
return;
|
||||
}
|
||||
Scheduler.runTaskLater(() -> {
|
||||
// remove all force loaded chunks
|
||||
int count = 0;
|
||||
for (World world : EssentialsD.instance.getServer().getWorlds()) {
|
||||
XLogger.debug("清除所有强加载区块: " + world.getName());
|
||||
Collection<Chunk> chunks = world.getForceLoadedChunks();
|
||||
for (Chunk chunk : chunks) {
|
||||
count++;
|
||||
world.setChunkForceLoaded(chunk.getX(), chunk.getZ(), false);
|
||||
}
|
||||
}
|
||||
XLogger.info("清除所有强加载区块: " + count);
|
||||
// world:0:0
|
||||
for (String s : _force_load_chunks) {
|
||||
String[] split = s.split(":");
|
||||
if (split.length != 3) {
|
||||
XLogger.warn("ForceLoadChunks 配置错误: " + s);
|
||||
continue;
|
||||
}
|
||||
String world_name = split[0];
|
||||
int x = Integer.parseInt(split[1]);
|
||||
int z = Integer.parseInt(split[2]);
|
||||
World world = _plugin.getServer().getWorld(world_name);
|
||||
if (world == null) {
|
||||
XLogger.warn("ForceLoadChunks 配置错误: 世界 " + world_name + " 不存在");
|
||||
continue;
|
||||
}
|
||||
world.setChunkForceLoaded(x, z, true);
|
||||
XLogger.info("标记强加载区块: " + world_name + " " + x + " " + z);
|
||||
}
|
||||
}, _chunk_operate_delay * 20);
|
||||
}
|
||||
|
||||
public Integer getHomeLimitAmount() {
|
||||
return _home_limit_amount;
|
||||
}
|
||||
|
||||
public List<String> getHomeWorldBlacklist() {
|
||||
return _home_world_blacklist;
|
||||
}
|
||||
|
||||
public String getDbType() {
|
||||
return _db_type;
|
||||
}
|
||||
|
||||
public void setDbType(String db_type) {
|
||||
_db_type = db_type;
|
||||
_file.set("Database.Type", db_type);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public String getDbHost() {
|
||||
return _db_host;
|
||||
}
|
||||
|
||||
public String getDbPort() {
|
||||
return _db_port;
|
||||
}
|
||||
|
||||
public String getDbName() {
|
||||
return _db_name;
|
||||
}
|
||||
|
||||
public void setDbUser(String db_user) {
|
||||
_db_user = db_user;
|
||||
_file.set("Database.User", db_user);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public String getDbUser() {
|
||||
if (_db_user.contains("@")) {
|
||||
setDbUser("'" + _db_user + "'");
|
||||
}
|
||||
return _db_user;
|
||||
}
|
||||
|
||||
public void setDbPass(String db_pass) {
|
||||
_db_pass = db_pass;
|
||||
_file.set("Database.Pass", db_pass);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public String getDbPass() {
|
||||
if (_db_pass.contains("@")) {
|
||||
setDbPass("'" + _db_pass + "'");
|
||||
}
|
||||
return _db_pass;
|
||||
}
|
||||
|
||||
public Boolean getPrefixEnable() {
|
||||
return _prefix_enable;
|
||||
}
|
||||
|
||||
public void setPrefixEnable(Boolean enable) {
|
||||
_prefix_enable = enable;
|
||||
_file.set("Prefix.Enable", enable);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public String getPrefixChatFormat() {
|
||||
return _prefix_chat_format;
|
||||
}
|
||||
|
||||
public void setPrefixChatFormat(String format) {
|
||||
_prefix_chat_format = format;
|
||||
_file.set("Prefix.Format", format);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public String getPrefixTabFormat() {
|
||||
return _prefix_tab_format;
|
||||
}
|
||||
|
||||
public void setPrefixTabFormat(String format) {
|
||||
_prefix_tab_format = format;
|
||||
_file.set("Prefix.TabFormat", format);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
|
||||
private final EssentialsD _plugin;
|
||||
private FileConfiguration _file;
|
||||
private boolean _debug;
|
||||
private float _exp_bottle_ratio;
|
||||
private Boolean _combine_exp_orbs_enable;
|
||||
private Float _combine_exp_orbs_radius;
|
||||
private Boolean _no_exp_cool_down;
|
||||
private List<String> _force_load_chunks;
|
||||
private Integer _chunk_operate_delay;
|
||||
private Integer _tp_tpa_expire;
|
||||
private Integer _tp_delay;
|
||||
private Integer _tp_cool_down;
|
||||
private Integer _tp_rtp_radius;
|
||||
private List<String> _tp_world_blacklist;
|
||||
private Boolean _chair_enable;
|
||||
private Integer _chair_max_width;
|
||||
private Boolean _chair_sign_check;
|
||||
private Float _chair_sit_height;
|
||||
private Boolean _check_update;
|
||||
// recipes
|
||||
private Boolean _recipes_crowbar;
|
||||
private Boolean _recipes_invisible_item_frame;
|
||||
private Boolean _recipes_light_block;
|
||||
private Boolean _recipes_stacked_enchant_book;
|
||||
|
||||
// home limit
|
||||
private Integer _home_limit_amount;
|
||||
private List<String> _home_world_blacklist;
|
||||
|
||||
// database
|
||||
private String _db_type;
|
||||
private String _db_host;
|
||||
private String _db_port;
|
||||
private String _db_user;
|
||||
private String _db_pass;
|
||||
private String _db_name;
|
||||
|
||||
// prefix
|
||||
private Boolean _prefix_enable;
|
||||
private String _prefix_chat_format;
|
||||
private String _prefix_tab_format;
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package cn.lunadeer.essentialsd.managers;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
|
||||
public class DatabaseTables {
|
||||
|
||||
public static void migrate() {
|
||||
String sql = "";
|
||||
|
||||
// player name
|
||||
sql = "CREATE TABLE IF NOT EXISTS player_name (" +
|
||||
" uuid VARCHAR(36) NOT NULL UNIQUE PRIMARY KEY," +
|
||||
" last_known_name TEXT NOT NULL" +
|
||||
");";
|
||||
EssentialsD.database.query(sql);
|
||||
|
||||
// login record
|
||||
sql = "CREATE TABLE IF NOT EXISTS login_record (" +
|
||||
" id SERIAL PRIMARY KEY," +
|
||||
" uuid VARCHAR(36) NOT NULL," +
|
||||
" ip VARCHAR(15) NOT NULL," +
|
||||
" login_time TIMESTAMP NOT NULL," +
|
||||
" logout_location TEXT," +
|
||||
" logout_time TIMESTAMP," +
|
||||
|
||||
" FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
|
||||
");";
|
||||
EssentialsD.database.query(sql);
|
||||
|
||||
// name record
|
||||
sql = "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" +
|
||||
");";
|
||||
EssentialsD.database.query(sql);
|
||||
|
||||
// tp record
|
||||
sql = "CREATE TABLE IF NOT EXISTS tp_record (" +
|
||||
" id SERIAL PRIMARY KEY," +
|
||||
" initiator_uuid VARCHAR(36) NOT NULL," +
|
||||
" from_location TEXT NOT NULL," +
|
||||
" to_location TEXT NOT NULL," +
|
||||
" type TEXT NOT NULL," +
|
||||
" success BOOLEAN NOT NULL," +
|
||||
" time TIMESTAMP NOT NULL," +
|
||||
|
||||
" FOREIGN KEY (initiator_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
|
||||
");";
|
||||
EssentialsD.database.query(sql);
|
||||
|
||||
// message record
|
||||
sql = "CREATE TABLE IF NOT EXISTS message_record (" +
|
||||
" id SERIAL PRIMARY KEY," +
|
||||
" sender_uuid VARCHAR(36) NOT NULL," +
|
||||
" message TEXT NOT NULL," +
|
||||
" time TIMESTAMP NOT NULL," +
|
||||
|
||||
" FOREIGN KEY (sender_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
|
||||
");";
|
||||
EssentialsD.database.query(sql);
|
||||
|
||||
// command record
|
||||
sql = "CREATE TABLE IF NOT EXISTS command_record (" +
|
||||
" id SERIAL PRIMARY KEY," +
|
||||
" executor_uuid VARCHAR(36) NOT NULL," +
|
||||
" command TEXT NOT NULL," +
|
||||
" time TIMESTAMP NOT NULL," +
|
||||
|
||||
" FOREIGN KEY (executor_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" +
|
||||
");";
|
||||
EssentialsD.database.query(sql);
|
||||
|
||||
// home info
|
||||
sql = "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" +
|
||||
");";
|
||||
EssentialsD.database.query(sql);
|
||||
|
||||
// warp info
|
||||
sql = "CREATE TABLE IF NOT EXISTS warp_point (" +
|
||||
" id SERIAL PRIMARY KEY," +
|
||||
" warp_name TEXT NOT NULL," +
|
||||
" location TEXT NOT NULL" +
|
||||
");";
|
||||
EssentialsD.database.query(sql);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package cn.lunadeer.essentialsd.managers;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TabListUpdater {
|
||||
|
||||
public static TabListUpdater instance;
|
||||
|
||||
public TabListUpdater() {
|
||||
instance = this;
|
||||
Scheduler.runTaskRepeatAsync(this::update, 0, 40);
|
||||
}
|
||||
|
||||
private void update() {
|
||||
Collection<? extends Player> players = Bukkit.getOnlinePlayers();
|
||||
Map<UUID, String> formatedNames = new HashMap<>();
|
||||
|
||||
int longestFormatedStringLength = 0;
|
||||
|
||||
for (Player player : players) {
|
||||
String formated = PlaceholderAPI.setPlaceholders(player, EssentialsD.config.getPrefixTabFormat());
|
||||
if (length(formated) > longestFormatedStringLength) {
|
||||
longestFormatedStringLength = length(formated);
|
||||
}
|
||||
formatedNames.put(player.getUniqueId(), formated);
|
||||
}
|
||||
// <span> 将会被替换为填充空格 " " 用于保证长度一致
|
||||
for (Player player : players) {
|
||||
String formated = formatedNames.get(player.getUniqueId());
|
||||
int formatedLength = length(formated);
|
||||
int spaceLength = longestFormatedStringLength - formatedLength;
|
||||
StringBuilder space = new StringBuilder();
|
||||
for (int i = 0; i < spaceLength; i++) {
|
||||
space.append(" ");
|
||||
}
|
||||
player.setPlayerListName(formated.replace("<span>", space.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int length(String value) {
|
||||
int valueLength = 0;
|
||||
String chinese = "[\u0391-\uFFE5]";
|
||||
/* 获取字段值的长度,如果含中文字符,则每个中文字符长度为2,否则为1 */
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
/* 获取一个字符 */
|
||||
String temp = value.substring(i, i + 1);
|
||||
/* 判断是否为中文字符 */
|
||||
if (temp.matches(chinese)) {
|
||||
/* 中文字符长度为2 */
|
||||
valueLength += 2;
|
||||
} else {
|
||||
/* 其他字符长度为1 */
|
||||
valueLength += 1;
|
||||
}
|
||||
}
|
||||
return valueLength;
|
||||
}
|
||||
}
|
@ -0,0 +1,328 @@
|
||||
package cn.lunadeer.essentialsd.managers;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.Scheduler;
|
||||
import cn.lunadeer.minecraftpluginutils.XLogger;
|
||||
import cn.lunadeer.minecraftpluginutils.stui.components.Button;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class TeleportManager {
|
||||
|
||||
private static final TextColor main_color = TextColor.color(0, 233, 255);
|
||||
|
||||
private final ConcurrentHashMap<UUID, TpTask> _tasks = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<UUID, LocalDateTime> _next_time_allow_tp = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<UUID, Location> _last_tp_location = new ConcurrentHashMap<>();
|
||||
|
||||
private static class TpTask {
|
||||
public Player initiator;
|
||||
public Player target;
|
||||
public UUID taskId;
|
||||
public Boolean tpahere = false;
|
||||
}
|
||||
|
||||
private boolean tpReqCheckFail(Player initiator, Player target) {
|
||||
if (initiator == target) {
|
||||
Notification.error(initiator, "不能传送到同一个位置");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!target.isOnline()) {
|
||||
Notification.error(initiator, "玩家 " + target.getName() + " 不在线");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (EssentialsD.config.getTpWorldBlackList().contains(target.getWorld().getName())) {
|
||||
Notification.error(initiator, "目的地所在世界 " + initiator.getWorld().getName() + " 不允许传送");
|
||||
return true;
|
||||
}
|
||||
|
||||
return CoolingDown(initiator);
|
||||
}
|
||||
|
||||
public void tpaRequest(Player initiator, Player target) {
|
||||
if (tpReqCheckFail(initiator, target)) return;
|
||||
|
||||
TpTask task = new TpTask();
|
||||
task.initiator = initiator;
|
||||
task.target = target;
|
||||
task.taskId = UUID.randomUUID();
|
||||
_tasks.put(task.taskId, task);
|
||||
|
||||
Notification.info(initiator, "已向 " + target.getName() + " 发送传送请求");
|
||||
|
||||
TextComponent acceptBtn = Button.createGreen("接受").setExecuteCommand("/tpa accept " + task.taskId).build();
|
||||
TextComponent denyBtn = Button.createRed("拒绝").setExecuteCommand("/tpa deny " + task.taskId).build();
|
||||
Notification.info(target, Component.text(" ", Style.style(main_color, TextDecoration.STRIKETHROUGH)));
|
||||
Notification.info(target, Component.text("| 玩家 " + initiator.getName() + " 请求传送到你的位置", main_color));
|
||||
Notification.info(target, Component.text("| 此请求将在 " + EssentialsD.config.getTpTpaExpire() + " 秒后失效", main_color));
|
||||
Notification.info(target, Component.text("| ", main_color).append(acceptBtn).append(Component.text(" ", main_color)).append(denyBtn));
|
||||
Notification.info(target, Component.text(" ", Style.style(main_color, TextDecoration.STRIKETHROUGH)));
|
||||
|
||||
Scheduler.runTaskLater(() -> {
|
||||
_tasks.remove(task.taskId);
|
||||
}, 20L * EssentialsD.config.getTpTpaExpire());
|
||||
}
|
||||
|
||||
public void tpahereRequest(Player initiator, Player target) {
|
||||
if (tpReqCheckFail(initiator, target)) return;
|
||||
|
||||
TpTask task = new TpTask();
|
||||
task.initiator = initiator;
|
||||
task.target = target;
|
||||
task.taskId = UUID.randomUUID();
|
||||
task.tpahere = true;
|
||||
_tasks.put(task.taskId, task);
|
||||
|
||||
Notification.info(initiator, "已向 " + target.getName() + " 发送传送请求");
|
||||
|
||||
TextComponent acceptBtn = Button.createGreen("接受").setExecuteCommand("/tpa accept " + task.taskId).build();
|
||||
TextComponent denyBtn = Button.createRed("拒绝").setExecuteCommand("/tpa deny " + task.taskId).build();
|
||||
Notification.info(target, Component.text(" ", Style.style(main_color, TextDecoration.STRIKETHROUGH)));
|
||||
Notification.info(target, Component.text("| 玩家 " + initiator.getName() + " 请求传送你到他的位置", main_color));
|
||||
Notification.info(target, Component.text("| 此请求将在 " + EssentialsD.config.getTpTpaExpire() + " 秒后失效", main_color));
|
||||
Notification.info(target, Component.text("| ", main_color).append(acceptBtn).append(Component.text(" ", main_color)).append(denyBtn));
|
||||
Notification.info(target, Component.text(" ", Style.style(main_color, TextDecoration.STRIKETHROUGH)));
|
||||
|
||||
Scheduler.runTaskLater(() -> {
|
||||
_tasks.remove(task.taskId);
|
||||
}, 20L * EssentialsD.config.getTpTpaExpire());
|
||||
}
|
||||
|
||||
public void deny(Player player, UUID taskId) {
|
||||
TpTask task = _tasks.get(taskId);
|
||||
if (task == null) {
|
||||
Notification.error(player, "传送请求不存在或已过期");
|
||||
return;
|
||||
}
|
||||
if (task.target != player) {
|
||||
Notification.error(player, "这不是你的传送请求");
|
||||
return;
|
||||
}
|
||||
_tasks.remove(taskId);
|
||||
if (task.initiator.isOnline()) {
|
||||
Notification.error(task.initiator, "玩家 " + player.getName() + " 拒绝了你的传送请求");
|
||||
}
|
||||
if (task.target.isOnline()) {
|
||||
Notification.error(player, "已拒绝 " + task.initiator.getName() + " 的传送请求");
|
||||
}
|
||||
}
|
||||
|
||||
public void accept(Player player, UUID taskId) {
|
||||
TpTask task = _tasks.get(taskId);
|
||||
if (task == null) {
|
||||
Notification.error(player, "传送请求不存在或已过期");
|
||||
return;
|
||||
}
|
||||
if (task.target != player) {
|
||||
Notification.error(player, "这不是你的传送请求");
|
||||
return;
|
||||
}
|
||||
_tasks.remove(taskId);
|
||||
|
||||
if (!task.initiator.isOnline() || !task.target.isOnline()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Notification.info(task.target, "已接受 " + task.initiator.getName() + " 的传送请求");
|
||||
Notification.info(task.initiator, "玩家 " + task.target.getName() + " 已接受你的传送请求");
|
||||
if (!task.tpahere) {
|
||||
try {
|
||||
doTeleportDelayed(task.initiator, task.target.getLocation(), EssentialsD.config.getTpDelay(), () -> {
|
||||
Notification.info(task.initiator, "正在传送到 " + task.initiator.getName() + " 的位置");
|
||||
}, () -> {
|
||||
Notification.info(task.initiator, "已传送到 " + task.initiator.getName() + " 的位置");
|
||||
Notification.info(task.target, "玩家 " + task.initiator.getName() + " 已传送到你的位置");
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
Notification.error(player, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
doTeleportDelayed(task.target, task.initiator.getLocation(), EssentialsD.config.getTpDelay(), () -> {
|
||||
Notification.info(task.target, "正在传送到 " + task.initiator.getName() + " 的位置");
|
||||
}, () -> {
|
||||
Notification.info(task.target, "已传送到 " + task.initiator.getName() + " 的位置");
|
||||
Notification.info(task.initiator, "玩家 " + task.target.getName() + " 已传送到你的位置");
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
Notification.error(player, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void back(Player player) {
|
||||
if (!_last_tp_location.containsKey(player.getUniqueId())) {
|
||||
Notification.error(player, "没有找到可返回的位置");
|
||||
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 (CoolingDown(player)) return;
|
||||
if (EssentialsD.config.getTpDelay() > 0) {
|
||||
Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后返回上次传送的位置");
|
||||
}
|
||||
try {
|
||||
doTeleportDelayed(player, target, EssentialsD.config.getTpDelay(), () -> {
|
||||
Notification.info(player, "正在返回上次传送的位置");
|
||||
}, () -> {
|
||||
Notification.info(player, "已返回上次传送的位置");
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
Notification.error(player, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void rtp(Player player) {
|
||||
if (EssentialsD.config.getTpWorldBlackList().contains(player.getWorld().getName())) {
|
||||
Notification.error(player, "此世界 " + player.getWorld().getName() + " 不允许传送");
|
||||
return;
|
||||
}
|
||||
if (CoolingDown(player)) return;
|
||||
int radius = EssentialsD.config.getTpRtpRadius();
|
||||
// get main world
|
||||
World world = null;
|
||||
for (World w : EssentialsD.instance.getServer().getWorlds()) {
|
||||
if (w.getEnvironment() == World.Environment.NORMAL) {
|
||||
world = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (world == null) {
|
||||
Notification.error(player, "未找到主世界");
|
||||
return;
|
||||
}
|
||||
int x = (int) (Math.random() * radius * 2) - radius + (int) player.getLocation().getX();
|
||||
int z = (int) (Math.random() * radius * 2) - radius + (int) player.getLocation().getZ();
|
||||
XLogger.debug("RTP: " + x + " " + z);
|
||||
Location location = new Location(world, x + 0.5, player.getY(), z + 0.5);
|
||||
try {
|
||||
doTeleportDelayed(player, location, EssentialsD.config.getTpDelay(), () -> {
|
||||
Notification.info(player, "正在传送到随机位置");
|
||||
}, () -> {
|
||||
Notification.info(player, "已传送到随机位置");
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
Notification.error(player, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
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 to, Long delay, Runnable before, Runnable after) {
|
||||
if (EssentialsD.config.getTpWorldBlackList().contains(to.getWorld().getName())) {
|
||||
Notification.error(player, "目的地所在世界 %s 不允许传送", to.getWorld().getName());
|
||||
return;
|
||||
}
|
||||
if (CoolingDown(player)) return;
|
||||
if (delay > 0) {
|
||||
Notification.info(player, "将在 %d 秒后执行传送", delay);
|
||||
Scheduler.runTaskAsync(() -> {
|
||||
long i = delay;
|
||||
while (i > 0) {
|
||||
if (!player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
Notification.actionBar(player, "传送倒计时 %d 秒", i);
|
||||
i--;
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
XLogger.warn(e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
Scheduler.runTaskLater(() -> {
|
||||
before.run();
|
||||
doTeleportSafely(player, to);
|
||||
after.run();
|
||||
}, 20L * delay);
|
||||
} else {
|
||||
before.run();
|
||||
doTeleportSafely(player, to);
|
||||
after.run();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean CoolingDown(Player player) {
|
||||
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);
|
||||
Notification.warn(player, "请等待 %d 秒后再次执行传送请求", secs_until_next);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把玩家传送到指定位置 并更新上次传送的位置
|
||||
*
|
||||
* @param player 玩家
|
||||
* @param location 位置
|
||||
*/
|
||||
public void doTeleportSafely(Player player, Location location) {
|
||||
if (CoolingDown(player)) return;
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
_next_time_allow_tp.put(player.getUniqueId(), now.plusSeconds(EssentialsD.config.getTpCoolDown()));
|
||||
location.getWorld().getChunkAtAsyncUrgently(location).thenAccept((chunk) -> {
|
||||
int max_attempts = 512;
|
||||
while (location.getBlock().isPassable()) {
|
||||
location.setY(location.getY() - 1);
|
||||
max_attempts--;
|
||||
if (max_attempts <= 0) {
|
||||
Notification.error(player, "传送目的地不安全,已取消传送");
|
||||
return;
|
||||
}
|
||||
}
|
||||
Block up1 = location.getBlock().getRelative(BlockFace.UP);
|
||||
Block up2 = up1.getRelative(BlockFace.UP);
|
||||
max_attempts = 512;
|
||||
while (!(up1.isPassable() && !up1.isLiquid()) || !(up2.isPassable() && !up2.isLiquid())) {
|
||||
location.setY(location.getY() + 1);
|
||||
up1 = location.getBlock().getRelative(BlockFace.UP);
|
||||
up2 = up1.getRelative(BlockFace.UP);
|
||||
max_attempts--;
|
||||
if (max_attempts <= 0) {
|
||||
Notification.error(player, "传送目的地不安全,已取消传送");
|
||||
return;
|
||||
}
|
||||
}
|
||||
location.setY(location.getY() + 1);
|
||||
if (location.getBlock().getRelative(BlockFace.DOWN).getType() == Material.LAVA) {
|
||||
Notification.error(player, "传送目的地不安全,已取消传送");
|
||||
return;
|
||||
}
|
||||
updateLastTpLocation(player);
|
||||
player.teleportAsync(location, PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||
});
|
||||
}
|
||||
|
||||
public void updateLastTpLocation(Player player) {
|
||||
_last_tp_location.put(player.getUniqueId(), player.getLocation());
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package cn.lunadeer.essentialsd.recipes;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.minecraftpluginutils.XLogger;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
|
44
src/main/java/cn/lunadeer/essentialsd/tuis/HomeList.java
Normal file
44
src/main/java/cn/lunadeer/essentialsd/tuis/HomeList.java
Normal file
@ -0,0 +1,44 @@
|
||||
package cn.lunadeer.essentialsd.tuis;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import cn.lunadeer.essentialsd.dtos.HomeInfo;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.stui.ListView;
|
||||
import cn.lunadeer.minecraftpluginutils.stui.components.Button;
|
||||
import cn.lunadeer.minecraftpluginutils.stui.components.Line;
|
||||
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.isEmpty()) {
|
||||
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("传送").setExecuteCommand("/home " + home.homeName).build())
|
||||
.append(Button.createRed("删除").setExecuteCommand("/delhome " + home.homeName + " " + page).build());
|
||||
view.add(line);
|
||||
}
|
||||
view.showOn(player, page);
|
||||
}
|
||||
}
|
45
src/main/java/cn/lunadeer/essentialsd/tuis/WarpList.java
Normal file
45
src/main/java/cn/lunadeer/essentialsd/tuis/WarpList.java
Normal file
@ -0,0 +1,45 @@
|
||||
package cn.lunadeer.essentialsd.tuis;
|
||||
|
||||
import cn.lunadeer.essentialsd.dtos.HomeInfo;
|
||||
import cn.lunadeer.essentialsd.dtos.WarpPoint;
|
||||
import cn.lunadeer.essentialsd.utils.LocUtil;
|
||||
import cn.lunadeer.minecraftpluginutils.Notification;
|
||||
import cn.lunadeer.minecraftpluginutils.stui.ListView;
|
||||
import cn.lunadeer.minecraftpluginutils.stui.components.Button;
|
||||
import cn.lunadeer.minecraftpluginutils.stui.components.Line;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WarpList {
|
||||
public static void show(CommandSender sender, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
Notification.warn(sender, "只有玩家可以使用此命令");
|
||||
return;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
List<WarpPoint> points = WarpPoint.selectAll();
|
||||
if (points.isEmpty()) {
|
||||
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, "/warps");
|
||||
view.title("Warp 列表");
|
||||
for (WarpPoint point : points) {
|
||||
Line line = Line.create()
|
||||
.append(Component.text(point.getName()).hoverEvent(Component.text(LocUtil.toString(point.getLocation()))))
|
||||
.append(Button.create("传送").setExecuteCommand("/warp " + point.getName()).build());
|
||||
view.add(line);
|
||||
}
|
||||
view.showOn(player, page);
|
||||
}
|
||||
}
|
@ -1,232 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigManager {
|
||||
public ConfigManager(EssentialsD plugin) {
|
||||
_plugin = plugin;
|
||||
_plugin.saveDefaultConfig();
|
||||
reload();
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
_plugin.reloadConfig();
|
||||
_file = _plugin.getConfig();
|
||||
_debug = _file.getBoolean("Debug", false);
|
||||
_check_update = _file.getBoolean("CheckUpdate", true);
|
||||
_exp_bottle_ratio = (float) _file.getDouble("ExpBottleRatio", 1.0);
|
||||
_force_load_chunks = _file.getStringList("ForceLoadChunks");
|
||||
_chunk_operate_delay = _file.getInt("ChunkOperateDelay", 10);
|
||||
_commands_tpa = _file.getBoolean("Commands.Tpa", true);
|
||||
_commands_rtp = _file.getBoolean("Commands.Rtp", true);
|
||||
_commands_back = _file.getBoolean("Commands.Back", true);
|
||||
_commands_enderchest = _file.getBoolean("Commands.EnderChest", true);
|
||||
_commands_suicide = _file.getBoolean("Commands.Suicide", true);
|
||||
_commands_hat = _file.getBoolean("Commands.Hat", true);
|
||||
_commands_showitem = _file.getBoolean("Commands.ShowItem", true);
|
||||
_commands_skull = _file.getBoolean("Commands.Skull", true);
|
||||
_tp_delay = _file.getInt("Teleport.Delay", 0);
|
||||
_tp_cool_down = _file.getInt("Teleport.CoolDown", 0);
|
||||
_tp_tpa_expire = _file.getInt("Teleport.TpaExpire", 30);
|
||||
_tp_rtp_radius = _file.getInt("Teleport.RtpRadius", 10000);
|
||||
_chair_enable = _file.getBoolean("Chair.Enable", true);
|
||||
_chair_max_width = _file.getInt("Chair.MaxWidth", 4);
|
||||
_chair_sign_check = _file.getBoolean("Chair.SignCheck", true);
|
||||
_chair_sit_height = (float) _file.getDouble("Chair.SitHeight", -0.95);
|
||||
_recipes_crowbar = _file.getBoolean("Recipes.CrowBar", true);
|
||||
_recipes_invisible_item_frame = _file.getBoolean("Recipes.InvisibleItemFrame", true);
|
||||
_recipes_light_block = _file.getBoolean("Recipes.LightBlock", true);
|
||||
_recipes_stacked_enchant_book = _file.getBoolean("Recipes.StackedEnchantBook", true);
|
||||
}
|
||||
|
||||
public Boolean isDebug() {
|
||||
return _debug;
|
||||
}
|
||||
|
||||
public void setDebug(Boolean debug) {
|
||||
_debug = debug;
|
||||
_file.set("Debug", debug);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Boolean getCheckUpdate() {
|
||||
return _check_update;
|
||||
}
|
||||
|
||||
public float getExpBottleRatio() {
|
||||
return _exp_bottle_ratio;
|
||||
}
|
||||
|
||||
public void setExpBottleRatio(float ratio) {
|
||||
_exp_bottle_ratio = ratio;
|
||||
_file.set("ExpBottleRatio", ratio);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Integer getChunkOperateDelay() {
|
||||
return _chunk_operate_delay;
|
||||
}
|
||||
|
||||
public void setChunkOperateDelay(Integer delay) {
|
||||
_chunk_operate_delay = delay;
|
||||
_file.set("ChunkOperateDelay", delay);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Integer getTpDelay() {
|
||||
return _tp_delay;
|
||||
}
|
||||
|
||||
public Integer getTpCoolDown() {
|
||||
return _tp_cool_down;
|
||||
}
|
||||
|
||||
public Integer getTpTpaExpire() {
|
||||
return _tp_tpa_expire;
|
||||
}
|
||||
|
||||
public Integer getTpRtpRadius() {
|
||||
return _tp_rtp_radius;
|
||||
}
|
||||
|
||||
public Boolean getCommandsTpa() {
|
||||
return _commands_tpa;
|
||||
}
|
||||
|
||||
public Boolean getCommandsRtp() {
|
||||
return _commands_rtp;
|
||||
}
|
||||
|
||||
public Boolean getCommandsBack() {
|
||||
return _commands_back;
|
||||
}
|
||||
|
||||
public Boolean getCommandsEnderchest() {
|
||||
return _commands_enderchest;
|
||||
}
|
||||
|
||||
public Boolean getCommandsSuicide() {
|
||||
return _commands_suicide;
|
||||
}
|
||||
|
||||
public Boolean getCommandsHat() {
|
||||
return _commands_hat;
|
||||
}
|
||||
|
||||
public Boolean getCommandsShowItem() {
|
||||
return _commands_showitem;
|
||||
}
|
||||
|
||||
public Boolean getCommandsSkull() {
|
||||
return _commands_skull;
|
||||
}
|
||||
|
||||
|
||||
public Boolean getChairEnable() {
|
||||
return _chair_enable;
|
||||
}
|
||||
|
||||
public Integer getChairMaxWidth() {
|
||||
return _chair_max_width;
|
||||
}
|
||||
|
||||
public Boolean getChairSignCheck() {
|
||||
return _chair_sign_check;
|
||||
}
|
||||
|
||||
public Float getChairSitHeight() {
|
||||
return _chair_sit_height;
|
||||
}
|
||||
|
||||
public Boolean getRecipesCrowbar() {
|
||||
return _recipes_crowbar;
|
||||
}
|
||||
|
||||
public Boolean getRecipesInvisibleItemFrame() {
|
||||
return _recipes_invisible_item_frame;
|
||||
}
|
||||
|
||||
public Boolean getRecipesLightBlock() {
|
||||
return _recipes_light_block;
|
||||
}
|
||||
|
||||
public Boolean getRecipesStackedEnchantBook() {
|
||||
return _recipes_stacked_enchant_book;
|
||||
}
|
||||
|
||||
public void ApplyForceLoadChunks() {
|
||||
if (_chunk_operate_delay < 0) {
|
||||
XLogger.info("加载区块操作已禁用");
|
||||
return;
|
||||
}
|
||||
EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> {
|
||||
// remove all force loaded chunks
|
||||
int count = 0;
|
||||
for (World world : EssentialsD.instance.getServer().getWorlds()) {
|
||||
XLogger.debug("清除所有强加载区块: " + world.getName());
|
||||
Collection<Chunk> chunks = world.getForceLoadedChunks();
|
||||
for (Chunk chunk : chunks) {
|
||||
count++;
|
||||
world.setChunkForceLoaded(chunk.getX(), chunk.getZ(), false);
|
||||
}
|
||||
}
|
||||
XLogger.info("清除所有强加载区块: " + count);
|
||||
// world:0:0
|
||||
for (String s : _force_load_chunks) {
|
||||
String[] split = s.split(":");
|
||||
if (split.length != 3) {
|
||||
XLogger.warn("ForceLoadChunks 配置错误: " + s);
|
||||
continue;
|
||||
}
|
||||
String world_name = split[0];
|
||||
int x = Integer.parseInt(split[1]);
|
||||
int z = Integer.parseInt(split[2]);
|
||||
World world = _plugin.getServer().getWorld(world_name);
|
||||
if (world == null) {
|
||||
XLogger.warn("ForceLoadChunks 配置错误: 世界 " + world_name + " 不存在");
|
||||
continue;
|
||||
}
|
||||
world.setChunkForceLoaded(x, z, true);
|
||||
XLogger.info("标记强加载区块: " + world_name + " " + x + " " + z);
|
||||
}
|
||||
}, _chunk_operate_delay * 20);
|
||||
}
|
||||
|
||||
|
||||
private final EssentialsD _plugin;
|
||||
private FileConfiguration _file;
|
||||
private boolean _debug;
|
||||
private float _exp_bottle_ratio;
|
||||
private List<String> _force_load_chunks;
|
||||
private Integer _chunk_operate_delay;
|
||||
private Integer _tp_tpa_expire;
|
||||
private Integer _tp_delay;
|
||||
private Integer _tp_cool_down;
|
||||
private Integer _tp_rtp_radius;
|
||||
private Boolean _chair_enable;
|
||||
private Integer _chair_max_width;
|
||||
private Boolean _chair_sign_check;
|
||||
private Float _chair_sit_height;
|
||||
private Boolean _check_update;
|
||||
// commands
|
||||
private Boolean _commands_tpa;
|
||||
private Boolean _commands_rtp;
|
||||
private Boolean _commands_back;
|
||||
private Boolean _commands_enderchest;
|
||||
private Boolean _commands_suicide;
|
||||
private Boolean _commands_hat;
|
||||
private Boolean _commands_showitem;
|
||||
private Boolean _commands_skull;
|
||||
// recipes
|
||||
private Boolean _recipes_crowbar;
|
||||
private Boolean _recipes_invisible_item_frame;
|
||||
private Boolean _recipes_light_block;
|
||||
private Boolean _recipes_stacked_enchant_book;
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class GiteaReleaseCheck {
|
||||
private static class GiteaRelease {
|
||||
public String tag_name;
|
||||
public String message;
|
||||
public String html_url;
|
||||
public String download_url;
|
||||
}
|
||||
|
||||
public GiteaReleaseCheck(JavaPlugin plugin, String giteaServer, String owner, String repo) {
|
||||
this.gitea_server = giteaServer;
|
||||
this.owner = owner;
|
||||
this.repo = repo;
|
||||
this.plugin = plugin;
|
||||
this.current_version = plugin.getPluginMeta().getVersion();
|
||||
// 异步每12小时检查一次更新
|
||||
plugin.getServer().getAsyncScheduler().runAtFixedRate(plugin, (instance) -> {
|
||||
getLatestRelease();
|
||||
if (auto_update) {
|
||||
downloadUpdate();
|
||||
}
|
||||
}, 10 + new Random().nextInt(10), 60 * 60 * 12 + new Random().nextInt(60),
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void enableAutoUpdate() {
|
||||
auto_update = true;
|
||||
}
|
||||
|
||||
private String repoReleases() {
|
||||
return gitea_server + "/api/v1/repos/" + owner + "/" + repo + "/releases";
|
||||
}
|
||||
|
||||
private String tag(String tagName) {
|
||||
return gitea_server + "/api/v1/repos/" + owner + "/" + repo + "/tags/" + tagName;
|
||||
}
|
||||
|
||||
private void getLatestRelease() {
|
||||
XLogger.info("================================");
|
||||
XLogger.info("正在检查更新...");
|
||||
// send get request to repoReleases()
|
||||
try {
|
||||
// 发送 GET 请求
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(repoReleases()).openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.connect();
|
||||
// 获取响应
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader =
|
||||
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
JSONArray releaseList = (JSONArray) new JSONParser().parse(builder.toString());
|
||||
JSONObject latestRelease = (JSONObject) releaseList.get(0);
|
||||
GiteaRelease release = new GiteaRelease();
|
||||
release.tag_name = (String) latestRelease.get("tag_name");
|
||||
release.message = (String) latestRelease.get("body");
|
||||
release.html_url = (String) latestRelease.get("html_url");
|
||||
JSONArray assets = (JSONArray) latestRelease.get("assets");
|
||||
if (assets.size() > 0) {
|
||||
JSONObject asset = (JSONObject) assets.get(0);
|
||||
release.download_url = (String) asset.get("browser_download_url");
|
||||
}
|
||||
latest_release = release;
|
||||
XLogger.debug("Latest release: " + latest_release.tag_name);
|
||||
XLogger.debug("Message: " + latest_release.message);
|
||||
XLogger.debug("Download URL: " + latest_release.download_url);
|
||||
XLogger.debug("HTML URL: " + latest_release.html_url);
|
||||
if (isNewVersion(current_version, latest_release.tag_name)) {
|
||||
XLogger.info("发现新版本:" + latest_release.tag_name);
|
||||
XLogger.info("版本信息:");
|
||||
String[] message = latest_release.message.split("\n");
|
||||
for (String line : message) {
|
||||
XLogger.info("\t" + line);
|
||||
}
|
||||
XLogger.info("下载页面:" + latest_release.html_url);
|
||||
} else {
|
||||
XLogger.info("当前已是最新版本:" + current_version);
|
||||
}
|
||||
XLogger.info("================================");
|
||||
} catch (Exception e) {
|
||||
XLogger.err("Failed to get latest release: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getTagMessage(String tagName) {
|
||||
try {
|
||||
// 发送 GET 请求
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(tag(tagName)).openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.connect();
|
||||
// 获取响应
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader =
|
||||
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
JSONObject tag = (JSONObject) new JSONParser().parse(builder.toString());
|
||||
return (String) tag.get("message");
|
||||
} catch (Exception e) {
|
||||
XLogger.debug("Failed to get tag message: " + e.getMessage());
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadUpdate() {
|
||||
if (latest_release == null) {
|
||||
getLatestRelease();
|
||||
if (latest_release == null)
|
||||
return;
|
||||
}
|
||||
if (!isNewVersion(current_version, latest_release.tag_name)) {
|
||||
XLogger.info("当前已是最新版本");
|
||||
return;
|
||||
}
|
||||
if (latest_release.download_url == null) {
|
||||
XLogger.err("下载地址不可用");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
XLogger.info("================================");
|
||||
XLogger.info("正在下载更新...");
|
||||
File pluginsFolder = plugin.getDataFolder().getParentFile();
|
||||
File newJarFile = new File(pluginsFolder, latest_release.download_url.substring(latest_release.download_url.lastIndexOf("/") + 1));
|
||||
// send get request to download_url
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(latest_release.download_url).openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.connect();
|
||||
// 获取响应写入文件到 newJarFile
|
||||
try (DataOutputStream outputStream = new DataOutputStream(Files.newOutputStream(newJarFile.toPath()))) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = connection.getInputStream().read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
}
|
||||
XLogger.info("更新下载完成");
|
||||
XLogger.info("新版本:" + latest_release.tag_name);
|
||||
XLogger.info("请删除旧版本插件,然后重启服务器。");
|
||||
XLogger.info("================================");
|
||||
} catch (Exception e) {
|
||||
XLogger.err("Failed to auto update: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String gitea_server;
|
||||
private String owner;
|
||||
private String repo;
|
||||
private JavaPlugin plugin;
|
||||
private String current_version;
|
||||
private GiteaRelease latest_release = null;
|
||||
private boolean auto_update = false;
|
||||
|
||||
private boolean isNewVersion(String current, String in_coming) {
|
||||
// 只保留数字和点号
|
||||
current = current.replaceAll("[^0-9.]", "");
|
||||
in_coming = in_coming.replaceAll("[^0-9.]", "");
|
||||
XLogger.debug("Current version: " + current);
|
||||
XLogger.debug("In-coming version: " + in_coming);
|
||||
String[] current_version = current.split("\\.");
|
||||
String[] in_coming_version = in_coming.split("\\.");
|
||||
for (int i = 0; i < Math.min(current_version.length, in_coming_version.length); i++) {
|
||||
int current_v = Integer.parseInt(current_version[i]);
|
||||
int in_coming_v = Integer.parseInt(in_coming_version[i]);
|
||||
if (current_v < in_coming_v) {
|
||||
return true;
|
||||
} else if (current_v > in_coming_v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return current_version.length < in_coming_version.length;
|
||||
}
|
||||
}
|
23
src/main/java/cn/lunadeer/essentialsd/utils/LocUtil.java
Normal file
23
src/main/java/cn/lunadeer/essentialsd/utils/LocUtil.java
Normal file
@ -0,0 +1,23 @@
|
||||
package cn.lunadeer.essentialsd.utils;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class LocUtil {
|
||||
|
||||
public static String toString(Location loc) {
|
||||
return String.format("[%s, %.2f, %.2f, %.2f, %.2f, %.2f]", loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
|
||||
}
|
||||
|
||||
public static Location fromString(String str) {
|
||||
String[] parts = str.substring(1, str.length() - 1).split(", ");
|
||||
return new Location(
|
||||
org.bukkit.Bukkit.getWorld(parts[0]),
|
||||
Double.parseDouble(parts[1]),
|
||||
Double.parseDouble(parts[2]),
|
||||
Double.parseDouble(parts[3]),
|
||||
Float.parseFloat(parts[4]),
|
||||
Float.parseFloat(parts[5])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,863 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class Metrics {
|
||||
private final Plugin plugin;
|
||||
|
||||
private final MetricsBase metricsBase;
|
||||
|
||||
/**
|
||||
* Creates a new Metrics instance.
|
||||
*
|
||||
* @param plugin Your plugin instance.
|
||||
* @param serviceId The id of the service. It can be found at <a
|
||||
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
||||
*/
|
||||
public Metrics(JavaPlugin plugin, int serviceId) {
|
||||
this.plugin = plugin;
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
if (!config.isSet("serverUuid")) {
|
||||
config.addDefault("enabled", true);
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
config.addDefault("logFailedRequests", false);
|
||||
config.addDefault("logSentData", false);
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
// Inform the server owners about bStats
|
||||
config
|
||||
.options()
|
||||
.header(
|
||||
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
|
||||
+ "many people use their plugin and their total player count. It's recommended to keep bStats\n"
|
||||
+ "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
|
||||
+ "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
|
||||
+ "anonymous.")
|
||||
.copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
// Load the data
|
||||
boolean enabled = config.getBoolean("enabled", true);
|
||||
String serverUUID = config.getString("serverUuid");
|
||||
boolean logErrors = config.getBoolean("logFailedRequests", false);
|
||||
boolean logSentData = config.getBoolean("logSentData", false);
|
||||
boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
metricsBase =
|
||||
new MetricsBase(
|
||||
"bukkit",
|
||||
serverUUID,
|
||||
serviceId,
|
||||
enabled,
|
||||
this::appendPlatformData,
|
||||
this::appendServiceData,
|
||||
null,
|
||||
//submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
|
||||
plugin::isEnabled,
|
||||
(message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
|
||||
(message) -> this.plugin.getLogger().log(Level.INFO, message),
|
||||
logErrors,
|
||||
logSentData,
|
||||
logResponseStatusText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the underlying scheduler service.
|
||||
*/
|
||||
public void shutdown() {
|
||||
metricsBase.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
metricsBase.addCustomChart(chart);
|
||||
}
|
||||
|
||||
private void appendPlatformData(JsonObjectBuilder builder) {
|
||||
builder.appendField("playerAmount", getPlayerAmount());
|
||||
builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0);
|
||||
builder.appendField("bukkitVersion", Bukkit.getVersion());
|
||||
builder.appendField("bukkitName", Bukkit.getName());
|
||||
builder.appendField("javaVersion", System.getProperty("java.version"));
|
||||
builder.appendField("osName", System.getProperty("os.name"));
|
||||
builder.appendField("osArch", System.getProperty("os.arch"));
|
||||
builder.appendField("osVersion", System.getProperty("os.version"));
|
||||
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
private void appendServiceData(JsonObjectBuilder builder) {
|
||||
builder.appendField("pluginVersion", plugin.getDescription().getVersion());
|
||||
}
|
||||
|
||||
private int getPlayerAmount() {
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed from an array to a collection,
|
||||
// This fixes java.lang.NoSuchMethodError:
|
||||
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
return onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
// Just use the new method if the reflection failed
|
||||
return Bukkit.getOnlinePlayers().size();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetricsBase {
|
||||
|
||||
/**
|
||||
* The version of the Metrics class.
|
||||
*/
|
||||
public static final String METRICS_VERSION = "3.0.2";
|
||||
|
||||
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s";
|
||||
|
||||
private final ScheduledExecutorService scheduler;
|
||||
|
||||
private final String platform;
|
||||
|
||||
private final String serverUuid;
|
||||
|
||||
private final int serviceId;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendPlatformDataConsumer;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendServiceDataConsumer;
|
||||
|
||||
private final Consumer<Runnable> submitTaskConsumer;
|
||||
|
||||
private final Supplier<Boolean> checkServiceEnabledSupplier;
|
||||
|
||||
private final BiConsumer<String, Throwable> errorLogger;
|
||||
|
||||
private final Consumer<String> infoLogger;
|
||||
|
||||
private final boolean logErrors;
|
||||
|
||||
private final boolean logSentData;
|
||||
|
||||
private final boolean logResponseStatusText;
|
||||
|
||||
private final Set<CustomChart> customCharts = new HashSet<>();
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
/**
|
||||
* Creates a new MetricsBase class instance.
|
||||
*
|
||||
* @param platform The platform of the service.
|
||||
* @param serviceId The id of the service.
|
||||
* @param serverUuid The server uuid.
|
||||
* @param enabled Whether or not data sending is enabled.
|
||||
* @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all platform-specific data.
|
||||
* @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all service-specific data.
|
||||
* @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be
|
||||
* used to delegate the data collection to a another thread to prevent errors caused by
|
||||
* concurrency. Can be {@code null}.
|
||||
* @param checkServiceEnabledSupplier A supplier to check if the service is still enabled.
|
||||
* @param errorLogger A consumer that accepts log message and an error.
|
||||
* @param infoLogger A consumer that accepts info log messages.
|
||||
* @param logErrors Whether or not errors should be logged.
|
||||
* @param logSentData Whether or not the sent data should be logged.
|
||||
* @param logResponseStatusText Whether or not the response status text should be logged.
|
||||
*/
|
||||
public MetricsBase(
|
||||
String platform,
|
||||
String serverUuid,
|
||||
int serviceId,
|
||||
boolean enabled,
|
||||
Consumer<JsonObjectBuilder> appendPlatformDataConsumer,
|
||||
Consumer<JsonObjectBuilder> appendServiceDataConsumer,
|
||||
Consumer<Runnable> submitTaskConsumer,
|
||||
Supplier<Boolean> checkServiceEnabledSupplier,
|
||||
BiConsumer<String, Throwable> errorLogger,
|
||||
Consumer<String> infoLogger,
|
||||
boolean logErrors,
|
||||
boolean logSentData,
|
||||
boolean logResponseStatusText) {
|
||||
ScheduledThreadPoolExecutor scheduler =
|
||||
new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics"));
|
||||
// We want delayed tasks (non-periodic) that will execute in the future to be
|
||||
// cancelled when the scheduler is shutdown.
|
||||
// Otherwise, we risk preventing the server from shutting down even when
|
||||
// MetricsBase#shutdown() is called
|
||||
scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
this.scheduler = scheduler;
|
||||
this.platform = platform;
|
||||
this.serverUuid = serverUuid;
|
||||
this.serviceId = serviceId;
|
||||
this.enabled = enabled;
|
||||
this.appendPlatformDataConsumer = appendPlatformDataConsumer;
|
||||
this.appendServiceDataConsumer = appendServiceDataConsumer;
|
||||
this.submitTaskConsumer = submitTaskConsumer;
|
||||
this.checkServiceEnabledSupplier = checkServiceEnabledSupplier;
|
||||
this.errorLogger = errorLogger;
|
||||
this.infoLogger = infoLogger;
|
||||
this.logErrors = logErrors;
|
||||
this.logSentData = logSentData;
|
||||
this.logResponseStatusText = logResponseStatusText;
|
||||
checkRelocation();
|
||||
if (enabled) {
|
||||
// WARNING: Removing the option to opt-out will get your plugin banned from
|
||||
// bStats
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
this.customCharts.add(chart);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
private void startSubmitting() {
|
||||
final Runnable submitTask =
|
||||
() -> {
|
||||
if (!enabled || !checkServiceEnabledSupplier.get()) {
|
||||
// Submitting data or service is disabled
|
||||
scheduler.shutdown();
|
||||
return;
|
||||
}
|
||||
if (submitTaskConsumer != null) {
|
||||
submitTaskConsumer.accept(this::submitData);
|
||||
} else {
|
||||
this.submitData();
|
||||
}
|
||||
};
|
||||
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven
|
||||
// distribution of requests on the
|
||||
// bStats backend. To circumvent this problem, we introduce some randomness into
|
||||
// the initial and second delay.
|
||||
// WARNING: You must not modify and part of this Metrics class, including the
|
||||
// submit delay or frequency!
|
||||
// WARNING: Modifying this code will get your plugin banned on bStats. Just
|
||||
// don't do it!
|
||||
long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
|
||||
long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
|
||||
scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
|
||||
scheduler.scheduleAtFixedRate(
|
||||
submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void submitData() {
|
||||
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
|
||||
appendPlatformDataConsumer.accept(baseJsonBuilder);
|
||||
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
|
||||
appendServiceDataConsumer.accept(serviceJsonBuilder);
|
||||
JsonObjectBuilder.JsonObject[] chartData =
|
||||
customCharts.stream()
|
||||
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(JsonObjectBuilder.JsonObject[]::new);
|
||||
serviceJsonBuilder.appendField("id", serviceId);
|
||||
serviceJsonBuilder.appendField("customCharts", chartData);
|
||||
baseJsonBuilder.appendField("service", serviceJsonBuilder.build());
|
||||
baseJsonBuilder.appendField("serverUUID", serverUuid);
|
||||
baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION);
|
||||
JsonObjectBuilder.JsonObject data = baseJsonBuilder.build();
|
||||
scheduler.execute(
|
||||
() -> {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Could not submit bStats metrics data", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendData(JsonObjectBuilder.JsonObject data) throws Exception {
|
||||
if (logSentData) {
|
||||
infoLogger.accept("Sent bStats metrics data: " + data.toString());
|
||||
}
|
||||
String url = String.format(REPORT_URL, platform);
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip");
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", "Metrics-Service/1");
|
||||
connection.setDoOutput(true);
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader =
|
||||
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
if (logResponseStatusText) {
|
||||
infoLogger.accept("Sent data to bStats and received response: " + builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the class was properly relocated.
|
||||
*/
|
||||
private void checkRelocation() {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null
|
||||
|| !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this
|
||||
// little "trick" ... :D
|
||||
final String defaultPackage =
|
||||
new String(new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
|
||||
final String examplePackage =
|
||||
new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure no one just copy & pastes the example and uses the wrong
|
||||
// package names
|
||||
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|
||||
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given string.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped string.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
valuesBuilder.appendField(entry.getKey(), new int[]{entry.getValue()});
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class CustomChart {
|
||||
|
||||
private final String chartId;
|
||||
|
||||
protected CustomChart(String chartId) {
|
||||
if (chartId == null) {
|
||||
throw new IllegalArgumentException("chartId must not be null");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
public JsonObjectBuilder.JsonObject getRequestJsonObject(
|
||||
BiConsumer<String, Throwable> errorLogger, boolean logErrors) {
|
||||
JsonObjectBuilder builder = new JsonObjectBuilder();
|
||||
builder.appendField("chartId", chartId);
|
||||
try {
|
||||
JsonObjectBuilder.JsonObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
builder.appendField("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception;
|
||||
}
|
||||
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extremely simple JSON builder.
|
||||
*
|
||||
* <p>While this class is neither feature-rich nor the most performant one, it's sufficient enough
|
||||
* for its use-case.
|
||||
*/
|
||||
public static class JsonObjectBuilder {
|
||||
|
||||
private StringBuilder builder = new StringBuilder();
|
||||
|
||||
private boolean hasAtLeastOneField = false;
|
||||
|
||||
public JsonObjectBuilder() {
|
||||
builder.append("{");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a null field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendNull(String key) {
|
||||
appendFieldUnescaped(key, "null");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("JSON value must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, "\"" + escape(value) + "\"");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int value) {
|
||||
appendFieldUnescaped(key, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param object The object.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject object) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("JSON object must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, object.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The string array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values)
|
||||
.map(value -> "\"" + escape(value) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a field to the object.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param escapedValue The escaped value of the field.
|
||||
*/
|
||||
private void appendFieldUnescaped(String key, String escapedValue) {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("JSON key must not be null");
|
||||
}
|
||||
if (hasAtLeastOneField) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
|
||||
hasAtLeastOneField = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the JSON string and invalidates this builder.
|
||||
*
|
||||
* @return The built JSON string.
|
||||
*/
|
||||
public JsonObject build() {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
JsonObject object = new JsonObject(builder.append("}").toString());
|
||||
builder = null;
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
|
||||
*
|
||||
* <p>This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
|
||||
* Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
|
||||
*
|
||||
* @param value The value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
private static String escape(String value) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
if (c == '"') {
|
||||
builder.append("\\\"");
|
||||
} else if (c == '\\') {
|
||||
builder.append("\\\\");
|
||||
} else if (c <= '\u000F') {
|
||||
builder.append("\\u000").append(Integer.toHexString(c));
|
||||
} else if (c <= '\u001F') {
|
||||
builder.append("\\u00").append(Integer.toHexString(c));
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A super simple representation of a JSON object.
|
||||
*
|
||||
* <p>This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
|
||||
* allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
|
||||
* JsonObject)}.
|
||||
*/
|
||||
public static class JsonObject {
|
||||
|
||||
private final String value;
|
||||
|
||||
private JsonObject(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class Notification {
|
||||
private static final Style i_style = Style.style(TextColor.color(139, 255, 123));
|
||||
private static final Style w_style = Style.style(TextColor.color(255, 185, 69));
|
||||
private static final Style e_style = Style.style(TextColor.color(255, 96, 72));
|
||||
|
||||
private static final String prefix = "[EssentialsD] ";
|
||||
|
||||
public static void info(Player player, String msg) {
|
||||
player.sendMessage(Component.text(prefix + msg, i_style));
|
||||
}
|
||||
|
||||
public static void warn(Player player, String msg) {
|
||||
player.sendMessage(Component.text(prefix + msg, w_style));
|
||||
}
|
||||
|
||||
public static void error(Player player, String msg) {
|
||||
player.sendMessage(Component.text(prefix + msg, e_style));
|
||||
}
|
||||
|
||||
public static void info(CommandSender sender, String msg) {
|
||||
sender.sendMessage(Component.text(prefix + msg, i_style));
|
||||
}
|
||||
|
||||
public static void warn(CommandSender sender, String msg) {
|
||||
sender.sendMessage(Component.text(prefix + msg, w_style));
|
||||
}
|
||||
|
||||
public static void error(CommandSender sender, String msg) {
|
||||
sender.sendMessage(Component.text(prefix + msg, e_style));
|
||||
}
|
||||
|
||||
public static void info(Player player, Component msg) {
|
||||
player.sendMessage(Component.text(prefix, i_style).append(msg));
|
||||
}
|
||||
|
||||
public static void warn(Player player, Component msg) {
|
||||
player.sendMessage(Component.text(prefix, w_style).append(msg));
|
||||
}
|
||||
|
||||
public static void error(Player player, Component msg) {
|
||||
player.sendMessage(Component.text(prefix, e_style).append(msg));
|
||||
}
|
||||
|
||||
public static void info(CommandSender player, Component msg) {
|
||||
player.sendMessage(Component.text(prefix, i_style).append(msg));
|
||||
}
|
||||
|
||||
public static void warn(CommandSender player, Component msg) {
|
||||
player.sendMessage(Component.text(prefix, w_style).append(msg));
|
||||
}
|
||||
|
||||
public static void error(CommandSender player, Component msg) {
|
||||
player.sendMessage(Component.text(prefix, e_style).append(msg));
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils.STUI;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
|
||||
public class Button {
|
||||
|
||||
public static TextComponent create(String text, String command) {
|
||||
return Component.text("[" + text + "]", ViewStyles.action_color)
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.clickEvent(net.kyori.adventure.text.event.ClickEvent.Action.RUN_COMMAND, command));
|
||||
}
|
||||
|
||||
public static TextComponent createRed(String text, String command) {
|
||||
return Component.text("[" + text + "]", ViewStyles.error_color)
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.clickEvent(net.kyori.adventure.text.event.ClickEvent.Action.RUN_COMMAND, command));
|
||||
}
|
||||
|
||||
public static TextComponent createGreen(String text, String command) {
|
||||
return Component.text("[" + text + "]", ViewStyles.success_color)
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.clickEvent(net.kyori.adventure.text.event.ClickEvent.Action.RUN_COMMAND, command));
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils.STUI;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class Line {
|
||||
private final List<Component> elements = new ArrayList<>();
|
||||
|
||||
private final TextComponent divider = Component.text(" - ", ViewStyles.sub_color);
|
||||
|
||||
public Line() {
|
||||
}
|
||||
|
||||
public TextComponent build() {
|
||||
TextComponent.Builder builder = Component.text();
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
builder.append(elements.get(i));
|
||||
if (i != elements.size() - 1) {
|
||||
builder.append(divider);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static Line create() {
|
||||
return new Line();
|
||||
}
|
||||
|
||||
public Line append(TextComponent component) {
|
||||
elements.add(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Line append(Component component) {
|
||||
elements.add(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Line append(String component) {
|
||||
elements.add(Component.text(component));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils.STUI;
|
||||
|
||||
import cn.lunadeer.essentialsd.utils.Notification;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ListView {
|
||||
|
||||
private final Integer page_size;
|
||||
private final List<Line> lines = new ArrayList<>();
|
||||
private String command = "";
|
||||
private final View view = View.create();
|
||||
|
||||
public ListView(int page_size, String command) {
|
||||
super();
|
||||
this.page_size = page_size;
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public static ListView create(int page_size, String command) {
|
||||
return new ListView(page_size, command);
|
||||
}
|
||||
|
||||
public ListView title(String title) {
|
||||
view.title(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListView title(String title, String subtitle) {
|
||||
view.title(title);
|
||||
view.subtitle(subtitle);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListView subtitle(String subtitle) {
|
||||
view.subtitle(subtitle);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListView add(Line line) {
|
||||
lines.add(line);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListView addLines(List<Line> lines) {
|
||||
this.lines.addAll(lines);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void showOn(Player player, Integer page) {
|
||||
int offset = (page - 1) * page_size;
|
||||
if (lines.isEmpty()) {
|
||||
Notification.warn(player, "没有数据");
|
||||
return;
|
||||
}
|
||||
if (offset >= lines.size() || offset < 0) {
|
||||
Notification.error(player, "页数超出范围");
|
||||
return;
|
||||
}
|
||||
for (int i = offset; i < offset + page_size; i++) {
|
||||
if (i >= lines.size()) {
|
||||
break;
|
||||
}
|
||||
view.addLine(lines.get(i));
|
||||
}
|
||||
view.actionBar(Pagination.create(page, lines.size(), this.command));
|
||||
view.showOn(player);
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils.STUI;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.lunadeer.essentialsd.utils.STUI.ViewStyles.main_color;
|
||||
import static cn.lunadeer.essentialsd.utils.STUI.ViewStyles.sub_color;
|
||||
|
||||
|
||||
public class Pagination {
|
||||
public static TextComponent create(int page, int item_size, String command) {
|
||||
// 第 x/y 页 [上一页] [下一页]
|
||||
int page_size = 4;
|
||||
int page_count = (int) Math.ceil((double) item_size / page_size);
|
||||
if (page_count == 0) {
|
||||
page_count = 1;
|
||||
}
|
||||
List<Component> componentList = new ArrayList<>();
|
||||
componentList.add(Component.text("第 ", main_color));
|
||||
componentList.add(Component.text(page, sub_color));
|
||||
componentList.add(Component.text("/", main_color));
|
||||
componentList.add(Component.text(page_count, sub_color));
|
||||
componentList.add(Component.text(" 页 ", main_color));
|
||||
if (page > 1) {
|
||||
componentList.add(Button.create("上一页", command + " " + (page - 1)));
|
||||
}
|
||||
if (page < page_count) {
|
||||
componentList.add(Button.create("下一页", command + " " + (page + 1)));
|
||||
}
|
||||
TextComponent.Builder builder = Component.text();
|
||||
for (Component component : componentList) {
|
||||
builder.append(component);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils.STUI;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.lunadeer.essentialsd.utils.STUI.ViewStyles.main_color;
|
||||
|
||||
public class View {
|
||||
protected TextComponent title_decorate = Component.text("━", main_color);
|
||||
protected TextComponent space = Component.text(" ");
|
||||
protected TextComponent sub_title_decorate = Component.text("- ", main_color);
|
||||
protected TextComponent line_decorate = Component.text("⌗ ", main_color);
|
||||
protected TextComponent action_decorate = Component.text("▸ ", main_color);
|
||||
protected TextComponent title = Component.text(" ");
|
||||
protected TextComponent subtitle = Component.text("");
|
||||
protected List<TextComponent> content_lines = new ArrayList<>();
|
||||
protected TextComponent actionbar = Component.text(" ");
|
||||
protected TextComponent edge = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color);
|
||||
protected TextComponent divide_line = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color);
|
||||
|
||||
public void showOn(Player player) {
|
||||
player.sendMessage(edge);
|
||||
TextComponent.Builder builder = Component.text();
|
||||
int title_length = title.content().length();
|
||||
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);
|
||||
for (int i = 0; i < decorate_count / 2; i++) {
|
||||
builder.append(title_decorate);
|
||||
}
|
||||
player.sendMessage(builder.build());
|
||||
if (subtitle.content().length() > 0) {
|
||||
player.sendMessage(divide_line);
|
||||
player.sendMessage(Component.text().append(sub_title_decorate).append(subtitle).build());
|
||||
}
|
||||
player.sendMessage(divide_line);
|
||||
for (TextComponent content_line : content_lines) {
|
||||
player.sendMessage(Component.text().append(line_decorate).append(content_line).build());
|
||||
}
|
||||
player.sendMessage(divide_line);
|
||||
player.sendMessage(Component.text().append(action_decorate).append(actionbar).build());
|
||||
player.sendMessage(edge);
|
||||
player.sendMessage(Component.text(" "));
|
||||
}
|
||||
|
||||
public static View create() {
|
||||
return new View();
|
||||
}
|
||||
|
||||
public View title(String title) {
|
||||
this.title = Component.text(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public View title(TextComponent title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public View subtitle(String subtitle) {
|
||||
this.subtitle = Component.text(subtitle);
|
||||
return this;
|
||||
}
|
||||
|
||||
public View subtitle(TextComponent subtitle) {
|
||||
this.subtitle = subtitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public View actionBar(TextComponent actionbar) {
|
||||
this.actionbar = actionbar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public View actionBar(String actionbar) {
|
||||
this.actionbar = Component.text(actionbar);
|
||||
return this;
|
||||
}
|
||||
|
||||
public View actionBar(Line actionbar) {
|
||||
this.actionbar = actionbar.build();
|
||||
return this;
|
||||
}
|
||||
|
||||
public View addLine(TextComponent component) {
|
||||
this.content_lines.add(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
public View addLine(String component) {
|
||||
this.content_lines.add(Component.text(component));
|
||||
return this;
|
||||
}
|
||||
|
||||
public View addLine(Line component) {
|
||||
this.content_lines.add(component.build());
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils.STUI;
|
||||
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
|
||||
public class ViewStyles {
|
||||
public static TextColor main_color = TextColor.color(0, 148, 213);
|
||||
public static TextColor sub_color = TextColor.color(122, 122, 122);
|
||||
public static TextColor action_color = TextColor.color(251, 255, 139);
|
||||
public static TextColor error_color = TextColor.color(255, 96, 72);
|
||||
public static TextColor success_color = TextColor.color(139, 255, 123);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils;
|
||||
|
||||
import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
|
||||
import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class Scheduler {
|
||||
public Scheduler(JavaPlugin plugin) {
|
||||
global = plugin.getServer().getGlobalRegionScheduler();
|
||||
async = plugin.getServer().getAsyncScheduler();
|
||||
}
|
||||
|
||||
public GlobalRegionScheduler global;
|
||||
public AsyncScheduler async;
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package cn.lunadeer.essentialsd.utils;
|
||||
|
||||
import cn.lunadeer.essentialsd.EssentialsD;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class XLogger {
|
||||
private static final EssentialsD _plugin = EssentialsD.instance;
|
||||
private static final Logger _logger = _plugin.getLogger();
|
||||
|
||||
private static final String prefix = "[EssentialsD] ";
|
||||
|
||||
public static void info(Player player, String message) {
|
||||
Notification.info(player, prefix + "I | " + message);
|
||||
if (EssentialsD.config.isDebug())
|
||||
debug("来自玩家[ " + player.getName() + " ] 的信息 | " + message);
|
||||
}
|
||||
|
||||
public static void info(String message) {
|
||||
_logger.info(" I | " + message);
|
||||
}
|
||||
|
||||
public static void warn(Player player, String message) {
|
||||
Notification.warn(player, prefix + "W | " + message);
|
||||
if (EssentialsD.config.isDebug())
|
||||
debug("来自玩家[ " + player.getName() + " ] 的警告 | " + message);
|
||||
}
|
||||
|
||||
public static void warn(String message) {
|
||||
_logger.warning(" W | " + message);
|
||||
}
|
||||
|
||||
public static void err(Player player, String message) {
|
||||
Notification.error(player, prefix + "E | " + message);
|
||||
if (EssentialsD.config.isDebug())
|
||||
debug("来自玩家[ " + player.getName() + " ] 的报错 | " + message);
|
||||
}
|
||||
|
||||
public static void err(String message) {
|
||||
_logger.severe(" E | " + message);
|
||||
}
|
||||
|
||||
public static void debug(Player player, String message) {
|
||||
if (!EssentialsD.config.isDebug()) return;
|
||||
if (player.isOp())
|
||||
Notification.info(player, prefix + "D | " + message);
|
||||
else
|
||||
debug("来自玩家[ " + player.getName() + " ] 的调试 | " + message);
|
||||
}
|
||||
|
||||
public static void debug(String message) {
|
||||
if (!EssentialsD.config.isDebug()) return;
|
||||
_logger.info(" D | " + message);
|
||||
}
|
||||
}
|
@ -1,8 +1,25 @@
|
||||
# 数据库配置
|
||||
Database:
|
||||
Type: sqlite # pgsql, sqlite
|
||||
Host: localhost # 数据库地址
|
||||
Port: 5432 # 数据库端口
|
||||
Name: essentials_d # 数据库名称
|
||||
User: essentials_d # 数据库用户名
|
||||
Pass: essentials_d # 数据库密码
|
||||
|
||||
# 附魔瓶经验值倍率
|
||||
ExpBottleRatio: 1.0
|
||||
|
||||
# 合并经验球
|
||||
CombineExpOrbs:
|
||||
Enable: false
|
||||
Radius: 1.5
|
||||
|
||||
# 经验吸收无冷却
|
||||
NoExpCoolDown: false
|
||||
|
||||
# 强加载区块列表
|
||||
ForceLoadChunks: []
|
||||
ForceLoadChunks: [ ]
|
||||
# - world:0:0
|
||||
# - world_the_end:-12:12
|
||||
|
||||
@ -18,23 +35,13 @@ Recipes:
|
||||
LightBlock: true # 光源方块
|
||||
StackedEnchantBook: true # 附魔书堆叠
|
||||
|
||||
# 扩展指令
|
||||
Commands:
|
||||
EnderChest: true # 快速末影箱
|
||||
Suicide: true # 自杀
|
||||
Hat: true # 把东西戴在头上
|
||||
ShowItem: true # 展示手中物品
|
||||
Skull: true # 获取头颅
|
||||
Tpa: true # 传送到其他玩家
|
||||
Rtp: true # 随机传送到附近
|
||||
Back: true # 回到上一个传送位置
|
||||
|
||||
# 传送
|
||||
Teleport:
|
||||
Delay: 0 # 传送延迟 秒
|
||||
CoolDown: 0 # 冷却时间 秒
|
||||
TpaExpire: 30 # 传送请求有效期 秒
|
||||
RtpRadius: 1000 # 随机传送最大半径
|
||||
Delay: 0 # 传送延迟 秒
|
||||
CoolDown: 0 # 冷却时间 秒
|
||||
TpaExpire: 30 # 传送请求有效期 秒
|
||||
RtpRadius: 1000 # 随机传送最大半径
|
||||
WorldBlackList: [ ] # 不允许传送的世界
|
||||
|
||||
# 把楼梯当作椅子使用
|
||||
Chair:
|
||||
@ -43,6 +50,17 @@ Chair:
|
||||
SignCheck: false
|
||||
SitHeight: -0.95
|
||||
|
||||
# home 相关限制
|
||||
HomeLimit:
|
||||
Amount: 5 # 数量限制
|
||||
WorldBlackList: [ ] # 不允许设置 home 的世界
|
||||
|
||||
# prefix (需要 PlaceholderAPI 支持)
|
||||
Prefix:
|
||||
Enable: false
|
||||
ChatFormat: '<%player_name%> '
|
||||
TabFormat: '%player_name%'
|
||||
|
||||
Debug: false
|
||||
|
||||
CheckUpdate: true
|
@ -1,66 +1,197 @@
|
||||
name: EssentialsD
|
||||
version: '${project.version}'
|
||||
main: cn.lunadeer.essentialsd.EssentialsD
|
||||
api-version: '1.20'
|
||||
api-version: '1.21'
|
||||
description: Deer's Essentials
|
||||
website: https://lunadeer.cn
|
||||
load: POSTWORLD
|
||||
load: STARTUP
|
||||
folia-supported: true
|
||||
softdepend:
|
||||
- LuckPerms
|
||||
- PlaceholderAPI
|
||||
permissions:
|
||||
essd.*:
|
||||
default: op
|
||||
description: 允许使用 EssentialsD 的所有功能
|
||||
essd.suicide:
|
||||
default: true
|
||||
essd.hat:
|
||||
default: true
|
||||
essd.showitem:
|
||||
default: true
|
||||
essd.skull:
|
||||
default: true
|
||||
essd.god:
|
||||
default: op
|
||||
essd.fly:
|
||||
default: op
|
||||
essd.time:
|
||||
default: op
|
||||
essd.save:
|
||||
default: op
|
||||
essd.more:
|
||||
default: op
|
||||
essd.weather:
|
||||
default: op
|
||||
essd.enderchest:
|
||||
default: true
|
||||
essd.tp.*:
|
||||
default: true
|
||||
children:
|
||||
essd.tp.tpa: true
|
||||
essd.tp.tpahere: true
|
||||
essd.tp.rtp: true
|
||||
essd.tp.back: true
|
||||
essd.inspect:
|
||||
default: op
|
||||
essd.home:
|
||||
default: true
|
||||
essd.setwarp:
|
||||
default: op
|
||||
essd.delwarp:
|
||||
default: op
|
||||
essd.warp:
|
||||
default: true
|
||||
|
||||
|
||||
commands:
|
||||
suicide:
|
||||
description: 自杀
|
||||
usage: /suicide
|
||||
permission: essd.suicide
|
||||
permission-message: 你不被允许自杀,快说“谢谢管理员”。
|
||||
hat:
|
||||
description: 将主手持的物品放在头部
|
||||
usage: /hat
|
||||
permission: essd.hat
|
||||
permission-message: 管理员不允许你随便在头上放东西(没权限)。
|
||||
showItem:
|
||||
description: 展示主手持的物品
|
||||
usage: /showitem
|
||||
permission: essd.showitem
|
||||
permission-message: 不要什么都想拿出来啊喂!(没权限)
|
||||
skull:
|
||||
description: 使用任意头颅交换获得一个自己的头颅
|
||||
usage: /skull
|
||||
permission: essd.skull
|
||||
permission-message: 禁止献祭(没权限)。
|
||||
god:
|
||||
description: 无敌模式
|
||||
usage: /god [player]
|
||||
permission: essd.god
|
||||
permission-message: 禁止成仙(没权限)。
|
||||
fly:
|
||||
description: 飞行模式
|
||||
usage: /fly [player]
|
||||
permission: essd.fly
|
||||
permission-message: 禁止起飞(没权限)。
|
||||
day:
|
||||
description: 设置时间为白天
|
||||
usage: /day
|
||||
permission: essd.time
|
||||
permission-message: 起床失败(没权限)。
|
||||
noon:
|
||||
description: 设置时间为中午
|
||||
usage: /noon
|
||||
permission: essd.time
|
||||
permission-message: 午休失败(没权限)。
|
||||
night:
|
||||
description: 设置时间为晚上
|
||||
usage: /night
|
||||
permission: essd.time
|
||||
permission-message: 禁止熬夜(没权限)。
|
||||
save:
|
||||
description: 保存
|
||||
usage: /save
|
||||
permission: essd.save
|
||||
permission-message: 保存失败(没权限)。
|
||||
more:
|
||||
description: 获取更多手持物品
|
||||
usage: /more [amount]
|
||||
permission: essd.more
|
||||
permission-message: more不出来了(没权限)。
|
||||
sun:
|
||||
description: 设置天气为晴天
|
||||
usage: /sun
|
||||
permission: essd.weather
|
||||
permission-message: 禁止晒太阳(没权限)。
|
||||
rain:
|
||||
description: 设置天气为雨天
|
||||
usage: /rain
|
||||
permission: essd.weather
|
||||
permission-message: 求雨失败(没权限)。
|
||||
storm:
|
||||
description: 设置天气为雷雨
|
||||
usage: /storm
|
||||
permission: essd.weather
|
||||
permission-message: (没权限)。
|
||||
enderchest:
|
||||
description: 通过指令打开末影箱
|
||||
usage: /enderchest
|
||||
permission: essd.enderchest
|
||||
permission-message: 你没有权限快速打开末影箱。
|
||||
tpa:
|
||||
description: 发起传送请求
|
||||
usage: /tpa <player>
|
||||
permission: essd.tp.tpa
|
||||
permission-message: 你没有权限发起传送请求。
|
||||
tpahere:
|
||||
description: 发起传送到自己的请求
|
||||
usage: /tpahere <player>
|
||||
permission: essd.tp.tpahere
|
||||
permission-message: 你没有权限发起传送请求。
|
||||
rtp:
|
||||
description: 随机传送到附近
|
||||
usage: /rtp
|
||||
permission: essd.tp.rtp
|
||||
permission-message: 你没有权限随机传送。
|
||||
back:
|
||||
description: 回到上一个传送点
|
||||
usage: /back
|
||||
permission: essd.tp.back
|
||||
permission-message: 你没有权限回到上一个传送点。
|
||||
inspect:
|
||||
description: 检查玩家背包
|
||||
usage: /inspect <player>
|
||||
usage: /inspect <player>
|
||||
permission: essd.inspect
|
||||
permission-message: 禁止偷窥(没权限)。
|
||||
home:
|
||||
description: 回家
|
||||
usage: /home [名称]
|
||||
permission: essd.home
|
||||
permission-message: 你没有权限使用home。
|
||||
homes:
|
||||
description: 查看家列表
|
||||
usage: /homes
|
||||
permission: essd.home
|
||||
permission-message: 你没有权限使用home。
|
||||
sethome:
|
||||
description: 设置家
|
||||
usage: /sethome [名称]
|
||||
permission: essd.home
|
||||
permission-message: 你没有权限使用home。
|
||||
delhome:
|
||||
description: 删除家
|
||||
usage: /delhome <名称>
|
||||
permission: essd.home
|
||||
permission-message: 你没有权限使用home。
|
||||
setwarp:
|
||||
description: 设置传送点
|
||||
usage: /setwarp <名称>
|
||||
permission: essd.setwarp
|
||||
permission-message: 你没有权限设置传送点。
|
||||
delwarp:
|
||||
description: 删除传送点
|
||||
usage: /delwarp <名称>
|
||||
permission: essd.delwarp
|
||||
permission-message: 你没有权限删除传送点。
|
||||
warp:
|
||||
description: 传送到传送点
|
||||
usage: /warp <名称>
|
||||
permission: essd.warp
|
||||
permission-message: 你没有权限传送到传送点。
|
||||
warps:
|
||||
description: 查看传送点列表
|
||||
usage: /warps
|
||||
permission: essd.warp
|
||||
permission-message: 你没有权限查看传送点列表。
|
||||
|
Loading…
Reference in New Issue
Block a user