1
mirror of https://github.com/CarmJos/TimeForFlight.git synced 2024-09-18 19:45:45 +00:00

项目完成,首次提交

This commit is contained in:
carm 2021-10-22 07:48:36 +08:00
parent 1f3a67d14c
commit 090329db19
32 changed files with 1029 additions and 550 deletions

30
.github/ISSUE_TEMPLATE/bugs_report.md vendored Normal file
View File

@ -0,0 +1,30 @@
---
name: 问题提交
about: 提交并描述问题,帮助我们对其进行检查与修复。
title: ''
labels: bug
assignees: ''
---
**问题简述**
用简短的话语描述一下大概问题。
**问题来源**
描述一下通过哪些操作才发现的问题,如:
1. 打开 '...'
2. 点击了 '....'
3. 出现了报错 '....'
**预期结果**(可选)
如果问题不发生,应该是什么情况
**问题截图/问题报错**
如果有报错或输出,请提供截图。
**操作环境**
请在后台输入 `version` 并复制相关输出。
**其他补充**
如有其他补充,可以在这里描述。

View File

@ -0,0 +1,20 @@
---
name: 功能需求
about: 希望我们提供更多的功能。
title: ''
labels: enhancement
assignees: ''
---
**功能简述**
简单的描述一下你想要的功能
**需求来源**
简单的描述一下为什么需要这个功能。
**功能参考**(可选)
如果有相关功能的参考,如文本、截图,请提供给我们。
**附加内容**
如果有什么小细节需要重点注意,请在这里告诉我们。

33
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,33 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Deploy
on:
# 支持手动触发构建
workflow_dispatch:
release:
# 创建release的时候触发
types: [ published ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: "Set up JDK"
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
cache: maven
server-id: github
server-username: MAVEN_USERNAME
server-password: MAVEN_TOKEN
- name: "Deploy"
run: mvn -B deploy --file pom.xml
env:
MAVEN_USERNAME: ${{ github.repository_owner }}
MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}}

38
.github/workflows/maven.yml vendored Normal file
View File

@ -0,0 +1,38 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Build
on:
# 支持手动触发构建
workflow_dispatch:
push:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: "Set up JDK"
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
cache: maven
server-id: github
server-username: MAVEN_USERNAME
server-password: MAVEN_TOKEN
- name: "Package"
run: mvn -B package --file pom.xml
env:
MAVEN_USERNAME: ${{ github.repository_owner }}
MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: "Target Stage"
run: mkdir staging && cp target/*.jar staging
- name: "Upload artifact"
uses: actions/upload-artifact@v2
with:
name: artifact
path: staging

6
.gitignore vendored
View File

@ -1,3 +1,5 @@
/.idea/
/*.iml
/*/*.iml
/target/
TimeForFlight.iml
.idea/
/*/taget/

View File

@ -1,22 +0,0 @@
stages:
- check
- build
# 构建 Job
check:
stage: check
tags:
- maven
script:
- mvn -v
build:
stage: build
tags:
- maven
script:
- mvn install
- ls ./target/*.jar
artifacts:
expire_in: 1 week
paths:
- ./target/*.jar

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Carm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

90
README.md Normal file
View File

@ -0,0 +1,90 @@
# TimeForFlight 限时飞行!
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/TimeForFlight)
[![License](https://img.shields.io/github/license/CarmJos/TimeForFlight)](https://opensource.org/licenses/mit-license.php)
[![Build](https://github.com/CarmJos/TimeForFlight/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/TimeForFlight/actions/workflows/maven.yml)
![Support](https://img.shields.io/badge/Minecraft-Java%201.12--Latest-yellow)
![](https://visitor-badge.glitch.me/badge?page_id=TimeForFlight.readme)
## 简介
本插件支持玩家通过在线游玩积攒飞行时间,并在需要的时候开关飞行!
玩家数据将存储为YAML。
本插件基于Spigot实现功能简单运行稳定**理论上支持全版本**。
本插件由 [桦木原Harmoland](https://www.mcbbs.net/thread-1028923-1-1.html) 请求本人开发,经过授权后开源。
## 依赖
- **[必须]** 插件本体基于 [Spigot-API](https://hub.spigotmc.org/stash/projects/SPIGOT) 、[BukkitAPI](http://bukkit.org/) 实现。
详细依赖列表可见 [Dependencies](https://github.com/CarmJos/TimeForFlight/network/dependencies) 。
## 指令
### 玩家指令
```text
# /toggleFly
- 开关飞行模式
```~~~~
### 管理员指令
```text
# /TimeForFlight set <玩家> <秒数>
- 设置玩家的飞行时间
# /TimeForFlight add <玩家> <秒数>
- 添加玩家的飞行时间
# /TimeForFlight remove <玩家> <秒数>
- 移除玩家的飞行时间
# /TimeForFlight get <玩家>
- 查看玩家的飞行时间
# /TimeForFlight clear <玩家>
- 清空玩家的飞行时间
# /TimeForFlight help
- 查看此帮助
```
## 权限
```yaml
permissions:
timeforflight.admin:
default: op
description: "管理员权限"
timeforflight.unlimited:
default: op
description: "无限制飞行,不计算飞行时间"
timeforflight.allowflight:
description: "拥有此权限的玩家允许飞行"
timeforflight.getflighttime:
description: "拥有次权限的玩家会获赠飞行时间"
```
## 支持与捐赠
若您觉得本插件做的不错,您可以捐赠支持我!
感谢您成为开源项目的支持者!
<img height=25% width=25% src="https://raw.githubusercontent.com/CarmJos/CarmJos/main/img/donate-code.jpg" />
## 开源协议
本项目源码采用 [The MIT License](https://opensource.org/licenses/mit-license.php) 开源协议。
> ### 关于 MIT 协议
> MIT 协议可能是几大开源协议中最宽松的一个,核心条款是:
>
> 该软件及其相关文档对所有人免费,可以任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。唯一的限制是,软件中必须包含上述版 权和许可提示。
>
> 这意味着:
> - 你可以自由使用,复制,修改,可以用于自己的项目。
> - 可以免费分发或用来盈利。
> - 唯一的限制是必须包含许可声明。
> - MIT 协议是所有开源许可中最宽松的一个,除了必须包含许可声明外,再无任何限制。
>
> *以上文字来自 [五种开源协议GPL,LGPL,BSD,MIT,Apache](https://www.oschina.net/question/54100_9455) 。*

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

236
pom.xml
View File

@ -4,63 +4,199 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.carmwork</groupId>
<artifactId>TimeForFlight</artifactId>
<version>1.0-SNAPSHOT</version>
<groupId>cc.carm.plugin</groupId>
<artifactId>timeforflight</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>Spigot</artifactId>
<version>1.16.1</version>
<scope>system</scope>
<systemPath>${basedir}/_lib/spigot-1.16.1.jar</systemPath>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>EssentialX</artifactId>
<version>2.18.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/_lib/EssentialsX-2.18.0.0.jar</systemPath>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>EssentialX-Protect</artifactId>
<version>2.18.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/_lib/EssentialsXProtect-2.18.0.0.jar</systemPath>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>EssentialX-Chat</artifactId>
<version>2.18.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/_lib/EssentialsXChat-2.18.0.0.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 指定jdk防止update project -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<!-- 项目编码-->
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<name>TimeForFlight</name>
<description>玩家通过在线游玩积攒飞行时间,并在需要的时候开关飞行!</description>
<url>https://github.com/CarmJos/TimeForFlight</url>
<issueManagement>
<system>GitHub Issues</system>
<url>${project.url}/issues</url>
</issueManagement>
<ciManagement>
<system>GitHub Actions</system>
<url>${project.url}/actions/workflows/maven.yml</url>
</ciManagement>
<developers>
<developer>
<id>CarmJos</id>
<name>Carm Jos</name>
<email>carm@carm.cc</email>
<roles>
<role>Main Developer</role>
</roles>
<url>https://work.carm.cc</url>
</developer>
</developers>
<licenses>
<license>
<name>The MIT License</name>
<url>https://opensource.org/licenses/mit-license.php</url>
</license>
</licenses>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>placeholder-api-repo</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<repository>
<id>oss-repo</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>luck-repo</id>
<url>https://repo.lucko.me/</url>
</repository>
<repository>
<id>maven-central</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/${project.artifactId}</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/${project.artifactId}</url>
</repository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.12.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.azbh111</groupId>
<artifactId>craftbukkit-1.12.2</artifactId>
<version>R</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.10.9</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArgument>-parameters</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>META-INF/*.txt</exclude>
</excludes>
</filter>
</filters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,43 @@
package cc.carm.plugin.timeforflight;
import cc.carm.plugin.timeforflight.commands.TimeForFlightCommand;
import cc.carm.plugin.timeforflight.commands.ToggleFlyCommand;
import cc.carm.plugin.timeforflight.managers.ConfigManager;
import cc.carm.plugin.timeforflight.managers.DataManager;
import cc.carm.plugin.timeforflight.listeners.PlayerListener;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.logging.Level;
public class Main extends JavaPlugin {
private static Main instance;
public static Main getInstance() {
return Main.instance;
}
@Override
public void onEnable() {
instance = this;
logInfo("加载配置文件中");
ConfigManager.loadConfig();
logInfo("注册指令");
Main.getInstance().getCommand("TimeForFlight").setExecutor(new TimeForFlightCommand());
Main.getInstance().getCommand("ToggleFly").setExecutor(new ToggleFlyCommand());
logInfo("注册监听器");
Bukkit.getPluginManager().registerEvents(new PlayerListener(),this);
logInfo("初始化数据管理");
DataManager.init();
}
public static void logInfo(String message) {
Main.getInstance().getLogger().log(Level.INFO, message);
}
}

View File

@ -0,0 +1,154 @@
package cc.carm.plugin.timeforflight.commands;
import cc.carm.plugin.timeforflight.models.UserData;
import cc.carm.plugin.timeforflight.utils.MessageParser;
import cc.carm.plugin.timeforflight.enums.Permissions;
import cc.carm.plugin.timeforflight.managers.DataManager;
import cc.carm.plugin.timeforflight.utils.TimeFormat;
import com.google.common.collect.ImmutableList;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
/**
* 管理员管理查看玩家飞行时间的指令
*/
public class TimeForFlightCommand implements CommandExecutor, TabCompleter {
public static boolean help(CommandSender sender) {
sendMessage(sender, "&6&l限时飞行 &7管理指令帮助");
sendMessage(sender, "&8- &fset <玩家> <秒数> &7设置玩家的飞行时间 ");
sendMessage(sender, "&8- &fadd <玩家> <秒数> &7添加玩家的飞行时间 ");
sendMessage(sender, "&8- &fremove <玩家> <秒数> &7移除玩家的飞行时间 ");
sendMessage(sender, "&8- &fget <玩家> &7查看玩家的飞行时间 ");
sendMessage(sender, "&8- &fclear <玩家> &7清空玩家的飞行时间 ");
sendMessage(sender, "&8- &fhelp &7查看此帮助 ");
return true;
}
public static boolean noPerm(CommandSender sender) {
sender.sendMessage(MessageParser.parseColor("&c抱歉&7但您没有这么做的权限。"));
return true;
}
public static void sendMessage(CommandSender sender, String message) {
sender.sendMessage(MessageParser.parseColor(message));
}
public static void sendMessage(Player player, String message) {
player.sendMessage(MessageParser.parseColor(message));
}
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
if (!sender.hasPermission(Permissions.ADMIN.toString())) return noPerm(sender);
if (args.length > 3 || args.length < 2) return help(sender);
String aim = args[0].toLowerCase();
if (args.length == 2) {
if (aim.equalsIgnoreCase("clear")) {
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
UserData targetData = DataManager.getData(target.getUniqueId());
if (targetData.isFileLoaded() && targetData.getRemainTime() > 0) {
targetData.setTime(0);
}
DataManager.unloadData(target.getUniqueId());
sendMessage(sender, "&f已清空玩家 " + target.getName() + " 的飞行时间。");
return true;
} else if (aim.equalsIgnoreCase("get")) {
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
UserData targetData = DataManager.getData(target.getUniqueId());
if (targetData.isFileLoaded() && targetData.getRemainTime() > 0) {
sendMessage(sender, "&f玩家" + target.getName() + " 的剩余飞行时间为 " + TimeFormat.getTimeString(targetData.getRemainTime()) + "");
} else {
sendMessage(sender, "&f玩家" + target.getName() + " 的剩余飞行时间为 " + TimeFormat.getTimeString(0) + "");
}
DataManager.unloadData(target.getUniqueId());
return true;
} else {
return help(sender);
}
} else if (args.length == 3) {
if (aim.equalsIgnoreCase("add") || aim.equalsIgnoreCase("remove") || aim.equalsIgnoreCase("set")) {
int value;
try {
value = Integer.parseInt(args[2]);
} catch (Exception ignore) {
sendMessage(sender, "请填写正确的数字。");
return true;
}
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
UserData targetData = DataManager.getData(target.getUniqueId());
if (aim.equalsIgnoreCase("add")) {
targetData.addTime(value);
sendMessage(sender, "成功为玩家 " + target.getName() + " 添加飞行时间 " + TimeFormat.getTimeString(value) + "");
} else if (aim.equalsIgnoreCase("remove")) {
targetData.removeTime(value);
sendMessage(sender, "成功为玩家 " + target.getName() + " 移除飞行时间 " + TimeFormat.getTimeString(value) + "");
} else if (aim.equalsIgnoreCase("set")) {
targetData.setTime(value);
sendMessage(sender, "成功设置玩家 " + target.getName() + " 的飞行时间 " + TimeFormat.getTimeString(value) + "");
}
DataManager.unloadData(target.getUniqueId());
return true;
} else {
return help(sender);
}
}
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (!sender.hasPermission(Permissions.ADMIN.toString())) {
return ImmutableList.of();
}
switch (args.length) {
case 1: {
List<String> completions = new ArrayList<>();
List<String> strings = new ArrayList<>();
strings.add("help");
strings.add("set");
strings.add("add");
strings.add("remove");
strings.add("clear");
for (String s : strings) {
if (StringUtil.startsWithIgnoreCase(s, args[0].toLowerCase())) {
completions.add(s);
}
}
return completions;
}
case 2: {
String aim = args[1];
if (aim.equalsIgnoreCase("add")
|| aim.equalsIgnoreCase("remove")
|| aim.equalsIgnoreCase("set")
|| aim.equalsIgnoreCase("clear")) {
List<String> completions = new ArrayList<>();
for (Player pl : Bukkit.getOnlinePlayers()) {
if (StringUtil.startsWithIgnoreCase(pl.getName(), args[1].toLowerCase())) {
completions.add(pl.getName());
}
}
return completions;
}
}
default:
return ImmutableList.of();
}
}
}

View File

@ -0,0 +1,122 @@
package cc.carm.plugin.timeforflight.commands;
import cc.carm.plugin.timeforflight.models.UserData;
import cc.carm.plugin.timeforflight.utils.MessageParser;
import cc.carm.plugin.timeforflight.enums.Permissions;
import cc.carm.plugin.timeforflight.managers.DataManager;
import cc.carm.plugin.timeforflight.utils.TimeFormat;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
/**
* 开关飞行的指令
*/
public class ToggleFlyCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(MessageParser.parseColor("该指令只允许玩家使用。"));
sender.sendMessage(MessageParser.parseColor("您可以输入 /togglefly <玩家名> 设置某个玩家的飞行状态。"));
return true;
}
Player player = (Player) sender;
UserData userData = DataManager.getData(player.getUniqueId());
if (args.length == 0 || !sender.isOp()) {
if (player.getAllowFlight()) {
// 在玩家开启飞行的状态之下
if (player.hasPermission(Permissions.ALLOW_FLIGHT.toString()) || player.hasPermission(Permissions.UNLIMITED.toString())) {
userData.stopFly(player);
player.sendMessage(MessageParser.parseColor("§f已为您关闭飞行状态剩余飞行时长 " + userData.getRemainTimeString() + ""));
return true;
} else {
sender.sendMessage(MessageParser.parseColor("&c抱歉&f但您没有使用该指令的权限。"));
}
} else {
//在玩家没有开启飞行的状态下
if (player.hasPermission(Permissions.UNLIMITED.toString())) {
player.setAllowFlight(true);
player.sendMessage(MessageParser.parseColor("&f您现在可以飞行了"));
return true;
} else if (player.hasPermission(Permissions.ALLOW_FLIGHT.toString())) {
if (userData.canFly()) {
userData.startFlyTask(player);
player.sendMessage(MessageParser.parseColor("&f您现在可以飞行了"));
player.sendMessage(MessageParser.parseColor("&f剩余飞行时长 &a" + TimeFormat.getTimeString(userData.getRemainTime())));
return true;
} else {
player.sendMessage(MessageParser.parseColor("&c您的飞行时长不足无法开启飞行。"));
return true;
}
} else {
player.sendMessage(MessageParser.parseColor("&c抱歉&f但您没有使用该指令的权限。"));
return true;
}
}
} else if (args.length == 1) {
if (!sender.isOp()) {
sender.sendMessage(MessageParser.parseColor("&c抱歉&f但您没有使用该指令的权限。"));
return true;
}
Player target = Bukkit.getPlayer(args[0]);
if (target == null) {
sender.sendMessage("玩家 " + args[0] + " 不在线。");
return true;
}
UserData targetData = DataManager.getData(target.getUniqueId());
if (target.getAllowFlight()) {
//在目标玩家正在飞行的状态下
if (target.hasPermission(Permissions.ALLOW_FLIGHT.toString()) || target.hasPermission(Permissions.UNLIMITED.toString())) {
userData.stopFly(target);
target.sendMessage(MessageParser.parseColor("§f已为您关闭飞行状态剩余飞行时长 " + userData.getRemainTimeString() + ""));
return true;
} else {
sender.sendMessage(MessageParser.parseColor("&c抱歉&f但您没有使用该指令的权限。"));
}
} else {
//在目标玩家没有飞行的状态下
if (target.hasPermission(Permissions.UNLIMITED.toString())) {
target.setAllowFlight(true);
target.sendMessage(MessageParser.parseColor("&f您现在可以飞行了"));
sender.sendMessage(MessageParser.parseColor("已为玩家 " + target.getName() + " 开启飞行。"));
return true;
} else if (target.hasPermission(Permissions.ALLOW_FLIGHT.toString())) {
if (targetData.canFly()) {
targetData.startFlyTask(target);
target.sendMessage(MessageParser.parseColor("&f您现在可以飞行了"));
target.sendMessage(MessageParser.parseColor("&f剩余飞行时长 &a" + TimeFormat.getTimeString(userData.getRemainTime())));
sender.sendMessage(MessageParser.parseColor("已为玩家 " + target.getName() + " 开启飞行。"));
return true;
} else {
target.sendMessage(MessageParser.parseColor("&c您的飞行时长不足无法开启飞行。"));
sender.sendMessage(MessageParser.parseColor("玩家 " + target.getName() + " 飞行时间不足。"));
return true;
}
} else {
sender.sendMessage(MessageParser.parseColor("玩家 " + target.getName() + " 没有飞行权限。"));
return true;
}
}
}
return true;
}
}

View File

@ -0,0 +1,20 @@
package cc.carm.plugin.timeforflight.enums;
public enum Permissions {
UNLIMITED("timeforflight.unlimited"),
ALLOW_FLIGHT("timeforflight.allowflight"),
GET_TIME("timeforflight.getflighttime"),
ADMIN("timeforflight.admin");
String permissionNode;
Permissions(String permissionNode) {
this.permissionNode = permissionNode;
}
public String getPermission() {
return permissionNode;
}
}

View File

@ -0,0 +1,35 @@
package cc.carm.plugin.timeforflight.listeners;
import cc.carm.plugin.timeforflight.managers.DataManager;
import cc.carm.plugin.timeforflight.models.UserData;
import cc.carm.plugin.timeforflight.enums.Permissions;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class PlayerListener implements Listener {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
UserData playerCache = DataManager.loadData(player.getUniqueId());
if (player.hasPermission(Permissions.GET_TIME.toString())
&& !player.hasPermission(Permissions.UNLIMITED.toString())) {
playerCache.startGivenTask(player);
}
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
UserData playerCache = DataManager.getData(player.getUniqueId());
player.setAllowFlight(false);
player.setFlying(false);
DataManager.unloadData(player.getUniqueId());
}
}

View File

@ -1,7 +1,7 @@
package com.carmwork.plugin.timeforflight.managers;
package cc.carm.plugin.timeforflight.managers;
import com.carmwork.plugin.timeforflight.Main;
import com.carmwork.plugin.timeforflight.utils.MessageParser;
import cc.carm.plugin.timeforflight.utils.MessageParser;
import cc.carm.plugin.timeforflight.Main;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.Objects;

View File

@ -1,37 +1,37 @@
package com.carmwork.plugin.timeforflight.managers;
package cc.carm.plugin.timeforflight.managers;
import com.carmwork.plugin.timeforflight.Main;
import com.carmwork.plugin.timeforflight.models.UserData;
import cc.carm.plugin.timeforflight.models.UserData;
import cc.carm.plugin.timeforflight.Main;
import java.io.File;
import java.util.*;
public class DataManager {
private static File userdatasFolder;
private static File userdataFolder;
/**
* 通过这个Map缓存玩家的数据
*/
public static Map<UUID, UserData> userDatas = new HashMap<>();
public static Map<UUID, UserData> userDataCaches = new HashMap<>();
public static void init() {
userdatasFolder = new File(Main.getInstance().getDataFolder() + File.separator + "userdatas");
if (!userdatasFolder.isDirectory() || !userdatasFolder.exists()) {
userdatasFolder.mkdir();
userdataFolder = new File(Main.getInstance().getDataFolder() + File.separator + "userdata");
if (!userdataFolder.isDirectory() || !userdataFolder.exists()) {
userdataFolder.mkdir();
}
}
public static UserData loadData(UUID uuid) {
UserData prefixCache = new UserData(uuid);
userDatas.put(uuid, prefixCache);
userDataCaches.put(uuid, prefixCache);
return prefixCache;
}
public static UserData getData(UUID uuid) {
return userDatas.getOrDefault(uuid, loadData(uuid));
return userDataCaches.getOrDefault(uuid, loadData(uuid));
}
public static void unloadData(UUID uuid) {
@ -40,13 +40,13 @@ public class DataManager {
data.stopTasks();
data.saveData();
userDatas.remove(uuid);
userDataCaches.remove(uuid);
}
}
public static boolean isDataLoaded(UUID uuid) {
return userDatas.containsKey(uuid);
return userDataCaches.containsKey(uuid);
}
/**
@ -56,7 +56,7 @@ public class DataManager {
* @return 是否已有数据
*/
public static boolean hasData(UUID uuid) {
return Arrays.stream(Objects.requireNonNull(userdatasFolder.listFiles()))
return Arrays.stream(Objects.requireNonNull(userdataFolder.listFiles()))
.anyMatch(file -> file.getName().startsWith(uuid.toString()));
}

View File

@ -0,0 +1,182 @@
package cc.carm.plugin.timeforflight.models;
import cc.carm.plugin.timeforflight.Main;
import cc.carm.plugin.timeforflight.enums.Permissions;
import cc.carm.plugin.timeforflight.managers.ConfigManager;
import cc.carm.plugin.timeforflight.utils.MessageParser;
import cc.carm.plugin.timeforflight.utils.TimeFormat;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
public class UserData {
UUID uuid;
private File dataFile;
private FileConfiguration data;
boolean fileLoaded;
private int remainTime;
private BukkitRunnable giveTask;
private BukkitRunnable flyTask;
public UserData(UUID uuid) {
this.uuid = uuid;
File userdatasFolder = new File(Main.getInstance().getDataFolder() + "/userdata");
if (!userdatasFolder.isDirectory() || !userdatasFolder.exists()) {
userdatasFolder.mkdir();
}
this.dataFile = new File(userdatasFolder, this.uuid + ".yml");
this.remainTime = 0;
this.fileLoaded = dataFile.exists();
if (fileLoaded) {
this.data = YamlConfiguration.loadConfiguration(dataFile);
readData();
}
}
public void startFlyTask(Player player) {
player.setAllowFlight(true);
flyTask = new BukkitRunnable() {
@Override
public void run() {
if (canFly()) {
removeTime(1);
if (getRemainTime() == 10) {
player.sendMessage(MessageParser.parseColor("§c注意§7您的飞行剩余时间仅剩10秒请您注意安全"));
}
} else {
player.sendMessage(MessageParser.parseColor("&7您已经用尽剩余的飞行时长已为您关闭飞行状态。"));
stopFly(player);
}
}
};
flyTask.runTaskTimer(Main.getInstance(), 0L, 20L);
}
public void stopFly(Player player) {
if (this.flyTask != null) {
this.flyTask.cancel();
}
player.setAllowFlight(false);
player.setFlying(false);
}
public void readData() {
this.remainTime = getData().getInt("remainTime");
}
public void addTime(int remainTime) {
this.remainTime += remainTime;
}
public void removeTime(int time) {
this.remainTime -= time;
}
public void setTime(int time){
this.remainTime = time;
}
public void addTimeInConfig() {
addTime(ConfigManager.getTimeGiven());
}
public boolean canFly() {
return getRemainTime() > 0;
}
public BukkitRunnable getGiveTask() {
return giveTask;
}
public void startGivenTask(Player player) {
giveTask = new BukkitRunnable() {
@Override
public void run() {
if (!player.isOnline()
|| !player.hasPermission(Permissions.GET_TIME.toString())
|| player.hasPermission(Permissions.UNLIMITED.toString())) {
cancel();
}
addTimeInConfig();
if (ConfigManager.isAlertEnabled()) {
player.sendMessage(ConfigManager.getAlertMessage());
}
}
};
getGiveTask().runTaskTimerAsynchronously(Main.getInstance(),
ConfigManager.getTimeInterval() * 20L,
ConfigManager.getTimeInterval() * 20L);
}
public void stopTasks() {
if (getGiveTask() != null) {
getGiveTask().cancel();
}
if (this.flyTask != null) {
this.flyTask.cancel();
}
}
public boolean isFileLoaded() {
return fileLoaded;
}
public File getDataFile() {
return dataFile;
}
public FileConfiguration getData() {
return data;
}
public int getRemainTime() {
return remainTime;
}
private void checkFile() {
if (!dataFile.exists()) {
try {
dataFile.createNewFile();
} catch (IOException ex) {
Bukkit.getLogger().info("Could not load file " + "/userdata/" + "yml" + ex);
}
}
if (!isFileLoaded()) {
this.data = YamlConfiguration.loadConfiguration(dataFile);
this.fileLoaded = true;
}
}
public String getRemainTimeString() {
return TimeFormat.getTimeString(getRemainTime());
}
public void saveData() {
checkFile();
getData().set("remainTime", getRemainTime());
try {
getData().save(dataFile);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,6 +1,4 @@
package com.carmwork.plugin.timeforflight.utils;
import net.md_5.bungee.api.chat.BaseComponent;
package cc.carm.plugin.timeforflight.utils;
public class MessageParser {

View File

@ -0,0 +1,23 @@
package cc.carm.plugin.timeforflight.utils;
public class TimeFormat {
public static String getTimeString(int time) {
int temp;
StringBuilder sb = new StringBuilder();
temp = time / 60 / 60 % 60;
sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");
temp = time % 3600 / 60;
sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");
temp = time % 3600 % 60;
sb.append((temp < 10) ? "0" + temp : "" + temp);
return sb.toString();
}
}

View File

@ -1,33 +0,0 @@
package com.carmwork.plugin.timeforflight;
import com.carmwork.plugin.timeforflight.commands.ToggleFlyCommand;
import com.carmwork.plugin.timeforflight.managers.ConfigManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.logging.Level;
public class Main extends JavaPlugin {
private static Main instance;
public static Main getInstance() {
return Main.instance;
}
@Override
public void onEnable() {
instance = this;
logInfo("加载配置文件中");
ConfigManager.loadConfig();
logInfo("注册指令");
Main.getInstance().getCommand("togglefly").setExecutor(new ToggleFlyCommand());
}
public static void logInfo(String message) {
Main.getInstance().getLogger().log(Level.INFO, message);
}
}

View File

@ -1,98 +0,0 @@
package com.carmwork.plugin.timeforflight.commands;
import com.carmwork.plugin.timeforflight.utils.MessageParser;
import com.google.common.collect.ImmutableList;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
/**
* 管理员管理查看玩家飞行时间的指令
*/
public class TimeForFlightCommand implements CommandExecutor, TabCompleter {
/*
* - set <player> <second> 设置玩家剩余的飞行时间
* - add <player> <second> 为玩家添加飞行时间
* - remove <player> <second> 移除玩家的飞行时间
* - clear <player> 情况玩家的飞行时间
*
* - help 查看此帮助
*/
public static boolean help(CommandSender sender) {
sendMessage(sender, "&6&l限时飞行 &7管理指令帮助");
return true;
}
public static boolean noPerm(CommandSender sender) {
sender.sendMessage(MessageParser.parseColor("&c抱歉&7但您没有这么做的权限。"));
return true;
}
public static void sendMessage(CommandSender sender, String message) {
sender.sendMessage(MessageParser.parseColor(message));
}
public static void sendMessage(Player player, String message) {
player.sendMessage(MessageParser.parseColor(message));
}
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
if (!sender.hasPermission("timeforflight.admin")) return noPerm(sender);
if (args.length > 3 || args.length < 1) return help(sender);
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (!sender.hasPermission("timeforflight.admin")) {
return ImmutableList.of();
}
switch (args.length) {
case 1: {
List<String> completions = new ArrayList<>();
List<String> strings = new ArrayList<>();
strings.add("help");
strings.add("set");
strings.add("add");
strings.add("remove");
strings.add("clear");
for (String s : strings) {
if (StringUtil.startsWithIgnoreCase(s, args[0].toLowerCase())) {
completions.add(s);
}
}
return completions;
}
case 2: {
String aim = args[1];
if (aim.equalsIgnoreCase("add")
|| aim.equalsIgnoreCase("remove")
|| aim.equalsIgnoreCase("set")
|| aim.equalsIgnoreCase("clear")) {
List<String> completions = new ArrayList<>();
for (Player pl : Bukkit.getOnlinePlayers()) {
if (StringUtil.startsWithIgnoreCase(pl.getName(), args[1].toLowerCase())) {
completions.add(pl.getName());
}
}
return completions;
}
}
default:
return ImmutableList.of();
}
}
}

View File

@ -1,92 +0,0 @@
package com.carmwork.plugin.timeforflight.commands;
import com.carmwork.plugin.timeforflight.managers.DataManager;
import com.carmwork.plugin.timeforflight.models.UserData;
import com.carmwork.plugin.timeforflight.utils.MessageParser;
import com.carmwork.plugin.timeforflight.utils.TimeFormat;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
/**
* 开关飞行的指令
*/
public class ToggleFlyCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
if (args.length == 0 || !sender.isOp()) {
if (!(sender instanceof Player)) {
sender.sendMessage(MessageParser.parseColor("该指令只允许玩家使用。"));
sender.sendMessage(MessageParser.parseColor("您可以输入 /togglefly <玩家名> 设置某个玩家的飞行状态。"));
return true;
}
Player player = (Player) sender;
UserData userData = DataManager.getData(player.getUniqueId());
if (player.hasPermission("timeforflight.unlimited")) {
player.setAllowFlight(true);
player.sendMessage(MessageParser.parseColor("&f您现在可以飞行了"));
return true;
} else if (player.hasPermission("timeforflight.allowflight")) {
if (userData.canFly()) {
userData.startFlyTask(player);
player.sendMessage(MessageParser.parseColor("&f您现在可以飞行了"));
player.sendMessage(MessageParser.parseColor("&f剩余飞行时长 &a" + TimeFormat.getTimeString(userData.getRemainTime())));
return true;
} else {
player.sendMessage(MessageParser.parseColor("&c您的飞行时长不足无法开启飞行。"));
return true;
}
} else {
player.sendMessage(MessageParser.parseColor("&c抱歉&f但您没有使用该指令的权限。"));
return true;
}
} else if (args.length == 1) {
if (!sender.isOp()) {
sender.sendMessage(MessageParser.parseColor("&c抱歉&f但您没有使用该指令的权限。"));
return true;
}
Player player = Bukkit.getPlayer(args[0]);
if (player == null) {
sender.sendMessage("玩家 " + args[0] + " 不在线。");
return true;
}
UserData userData = DataManager.getData(player.getUniqueId());
if (player.hasPermission("timeforflight.unlimited")) {
player.setAllowFlight(true);
player.sendMessage(MessageParser.parseColor("&f您现在可以飞行了"));
sender.sendMessage(MessageParser.parseColor("已为玩家 " + player.getName() + " 开启飞行。"));
return true;
} else if (player.hasPermission("timeforflight.allowflight")) {
if (userData.canFly()) {
userData.startFlyTask(player);
player.sendMessage(MessageParser.parseColor("&f您现在可以飞行了"));
player.sendMessage(MessageParser.parseColor("&f剩余飞行时长 &a" + TimeFormat.getTimeString(userData.getRemainTime())));
sender.sendMessage(MessageParser.parseColor("已为玩家 " + player.getName() + " 开启飞行。"));
return true;
} else {
player.sendMessage(MessageParser.parseColor("&c您的飞行时长不足无法开启飞行。"));
sender.sendMessage(MessageParser.parseColor("玩家 " + player.getName() + " 飞行时间不足。"));
return true;
}
} else {
sender.sendMessage(MessageParser.parseColor("玩家 " + player.getName() + " 没有飞行权限。"));
return true;
}
}
return true;
}
}

View File

@ -1,35 +0,0 @@
package com.carmwork.plugin.timeforflight.listeners;
import com.carmwork.plugin.timeforflight.managers.DataManager;
import com.carmwork.plugin.timeforflight.models.UserData;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class PlayerListener implements Listener {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
UserData playerCache = DataManager.loadData(player.getUniqueId());
if (player.hasPermission("timeforflight.getflighttime")
&& !player.hasPermission("timeforflight.unlimited")) {
playerCache.startGivenTask(player);
}
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
UserData playerCache = DataManager.getData(player.getUniqueId());
player.setAllowFlight(false);
player.setFlying(false);
DataManager.unloadData(player.getUniqueId());
}
}

View File

@ -1,167 +0,0 @@
package com.carmwork.plugin.timeforflight.models;
import com.carmwork.plugin.timeforflight.Main;
import com.carmwork.plugin.timeforflight.managers.ConfigManager;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
public class UserData {
UUID uuid;
private File datafile;
private FileConfiguration data;
boolean fileLoaded;
private int remainTime;
private BukkitRunnable giveTask;
private BukkitRunnable flyTask;
public UserData(UUID uuid) {
this.uuid = uuid;
File userdatasFolder = new File(Main.getInstance().getDataFolder() + "/userdatas");
if (!userdatasFolder.isDirectory() || !userdatasFolder.exists()) {
userdatasFolder.mkdir();
}
this.datafile = new File(userdatasFolder, this.uuid + ".yml");
this.remainTime = 0;
this.fileLoaded = datafile.exists();
if (fileLoaded) {
this.data = YamlConfiguration.loadConfiguration(datafile);
readData();
}
}
public void startFlyTask(Player player) {
player.setAllowFlight(true);
flyTask = new BukkitRunnable() {
@Override
public void run() {
if (canFly()) {
removeTime(1);
} else {
player.setAllowFlight(false);
player.setFlying(false);
}
}
};
flyTask.runTaskTimer(Main.getInstance(), 0L, 20L);
}
public void stopFly() {
if (this.flyTask != null) {
this.flyTask.cancel();
}
}
public void readData() {
this.remainTime = getData().getInt("remainTime");
}
public void addTime(int remainTime) {
this.remainTime += remainTime;
}
public void removeTime(int time) {
this.remainTime -= time;
}
public void addTimeInConfig() {
addTime(ConfigManager.getTimeGiven());
}
public boolean canFly() {
return getRemainTime() > 0;
}
public BukkitRunnable getGiveTask() {
return giveTask;
}
public void startGivenTask(Player player) {
giveTask = new BukkitRunnable() {
@Override
public void run() {
if (!player.isOnline()
|| !player.hasPermission("timeforflight.getflighttime")
|| player.hasPermission("timeforflight.unlimited")) {
cancel();
}
addTimeInConfig();
if (ConfigManager.isAlertEnabled()) {
player.sendMessage(ConfigManager.getAlertMessage());
}
}
};
getGiveTask().runTaskTimerAsynchronously(Main.getInstance(),
ConfigManager.getTimeInterval() * 20,
ConfigManager.getTimeInterval() * 20);
}
public void stopTasks() {
if (getGiveTask() != null) {
getGiveTask().cancel();
}
if (this.flyTask != null) {
this.flyTask.cancel();
}
}
public boolean isFileLoaded() {
return fileLoaded;
}
public File getDatafile() {
return datafile;
}
public FileConfiguration getData() {
return data;
}
public int getRemainTime() {
return remainTime;
}
private void checkFile() {
if (!datafile.exists()) {
try {
datafile.createNewFile();
} catch (IOException ex) {
Bukkit.getLogger().info("Could not load file " + "/userdatas/" + "yml" + ex);
}
}
if (!isFileLoaded()) {
this.data = YamlConfiguration.loadConfiguration(datafile);
this.fileLoaded = true;
}
}
public void saveData() {
checkFile();
getData().set("remainTime", getRemainTime());
try {
getData().save(datafile);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,23 +0,0 @@
package com.carmwork.plugin.timeforflight.utils;
public class TimeFormat {
public static String getTimeString(int time) {
int temp;
StringBuilder sb = new StringBuilder();
temp = time / 60 / 60 % 60;
sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");
temp = time % 3600 / 60;
sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");
temp = time % 3600 % 60;
sb.append((temp < 10) ? "0" + temp : "" + temp);
return sb.toString();
}
}

View File

@ -1,6 +1,3 @@
#权限节点
#
settings:
interval: 60 #赠送飞行时间的在线时长间隔(秒)

View File

@ -1,8 +1,12 @@
name: TimeForFlight
main: com.carmwork.plugin.timeforflight.Main
version: 1.0.0 - SNAPSHOT
main: cc.carm.plugin.timeforflight.Main
name: ${project.name}
version: ${project.version}
description: ${project.description}
author: CarmJos
depend: [ProtocolLib]
website: ${project.url}
commands:
TimeForFlight:
permission: timeforflight.admin
@ -13,7 +17,10 @@ commands:
ToggleFly:
aliases:
- ToggleFlight
permissions:
timeforflight.admin:
default: op
timeforflight.unlimited:
default: op
description: "无限制飞行,不计算飞行时间"