feat(hamster-tpa): 完成 TPA 插件

This commit is contained in:
2023-06-05 19:30:37 +08:00
parent e83fc58925
commit f61a27bf99
13 changed files with 581 additions and 0 deletions

7
hamster-tpa/build.gradle Normal file
View File

@@ -0,0 +1,7 @@
version = '1.0.0'
setArchivesBaseName("HamsterTPA")
dependencies {
compileOnly "cn.hamster3.mc.plugin.core:bukkit:${hamster_ball_version}"
compileOnly "cn.hamster3.mc.plugin.ball:common:${hamster_ball_version}"
}

View File

@@ -0,0 +1,34 @@
package cn.hamster3.mc.plugin.tpa;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.tpa.command.TPACommand;
import cn.hamster3.mc.plugin.tpa.command.TPAcceptCommand;
import cn.hamster3.mc.plugin.tpa.command.TPDenyCommand;
import cn.hamster3.mc.plugin.tpa.config.ConfigManager;
import cn.hamster3.mc.plugin.tpa.core.DataManager;
import cn.hamster3.mc.plugin.tpa.listener.MainListener;
import org.bukkit.plugin.java.JavaPlugin;
public class HamsterTPAPlugin extends JavaPlugin {
public static final String BALL_CHANNEL = "HamsterTPA";
private static HamsterTPAPlugin instance;
public static HamsterTPAPlugin getInstance() {
return instance;
}
@Override
public void onLoad() {
instance = this;
ConfigManager.init();
DataManager.init();
}
@Override
public void onEnable() {
BallAPI.getInstance().addListener(MainListener.INSTANCE);
TPACommand.INSTANCE.hook();
TPAcceptCommand.INSTANCE.hook();
TPDenyCommand.INSTANCE.hook();
}
}

View File

@@ -0,0 +1,102 @@
package cn.hamster3.mc.plugin.tpa.command;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo;
import cn.hamster3.mc.plugin.core.bukkit.command.ParentCommand;
import cn.hamster3.mc.plugin.core.bukkit.constant.CoreMessage;
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
import cn.hamster3.mc.plugin.tpa.HamsterTPAPlugin;
import cn.hamster3.mc.plugin.tpa.config.ConfigManager;
import cn.hamster3.mc.plugin.tpa.core.DataManager;
import cn.hamster3.mc.plugin.tpa.core.Message;
import cn.hamster3.mc.plugin.tpa.thread.TimeoutThread;
import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public final class TPACommand extends ParentCommand {
public static final TPACommand INSTANCE = new TPACommand();
private TPACommand() {
super("hamster-tpa");
}
@Override
public @NotNull JavaPlugin getPlugin() {
return HamsterTPAPlugin.getInstance();
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!(sender instanceof Player)) {
CoreMessage.COMMAND_MUST_USED_BY_PLAYER.show(sender);
return true;
}
Player player = (Player) sender;
UUID uuid = player.getUniqueId();
if (args.length < 1) {
Message.TPA_NOT_INPUT_PLAYER_NAME.show(sender);
return true;
}
long now = System.currentTimeMillis();
long cdTime = DataManager.getPlayerUseTime(uuid) + ConfigManager.getTpaCD();
if (cdTime >= now) {
Message.TPA_IN_CD.show(player, TextReplacementConfig.builder()
.matchLiteral("%cd_time%")
.replacement(String.valueOf((cdTime - now) / 1000))
.build());
return true;
}
BallPlayerInfo playerInfo = BallAPI.getInstance().getPlayerInfo(args[0]);
if (playerInfo == null) {
Message.TPA_TARGET_PLAYER_NOT_ONLINE.show(player);
return true;
}
if (!playerInfo.isOnline()) {
Message.TPA_TARGET_PLAYER_NOT_ONLINE.show(player);
return true;
}
DataManager.setPlayerUseTime(uuid, now, true);
DataManager.sendTPA(uuid, playerInfo.getUuid(), now, true);
Message.TPA_SEND_SUCCESS.show(sender, TextReplacementConfig.builder()
.matchLiteral("%player_name%")
.replacement(playerInfo.getName())
.build());
BallAPI.getInstance().sendMessageToPlayer(
playerInfo.getUuid(),
Message.TPA_RECEIVED.getMessage().replace(TextReplacementConfig.builder()
.matchLiteral("%player_name%")
.replacement(player.getName())
.build()),
false);
CoreUtils.SCHEDULED_EXECUTOR.schedule(
new TimeoutThread(uuid, playerInfo.getUuid()),
ConfigManager.getTimeout(),
TimeUnit.MILLISECONDS
);
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
if (args.length == 1) {
String lowerCase = args[0].toLowerCase();
return BallAPI.getInstance().getAllPlayerInfo().values()
.stream()
.filter(BallPlayerInfo::isOnline)
.map(BallPlayerInfo::getName)
.filter(name -> name.toLowerCase().startsWith(lowerCase))
.collect(Collectors.toList());
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,63 @@
package cn.hamster3.mc.plugin.tpa.command;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.core.bukkit.command.ParentCommand;
import cn.hamster3.mc.plugin.core.bukkit.constant.CoreMessage;
import cn.hamster3.mc.plugin.tpa.HamsterTPAPlugin;
import cn.hamster3.mc.plugin.tpa.config.ConfigManager;
import cn.hamster3.mc.plugin.tpa.core.DataManager;
import cn.hamster3.mc.plugin.tpa.core.Message;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public final class TPAcceptCommand extends ParentCommand {
public static final TPAcceptCommand INSTANCE = new TPAcceptCommand();
private TPAcceptCommand() {
super("hamster-tpaccept");
}
@Override
public @NotNull JavaPlugin getPlugin() {
return HamsterTPAPlugin.getInstance();
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!(sender instanceof Player)) {
CoreMessage.COMMAND_MUST_USED_BY_PLAYER.show(sender);
return true;
}
Player player = (Player) sender;
UUID uuid = player.getUniqueId();
long now = System.currentTimeMillis();
List<UUID> list = DataManager.getPlayerReceivedTpa(uuid).entrySet()
.stream()
.filter(o -> o.getValue() + ConfigManager.getTimeout() >= now)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (list.isEmpty()) {
Message.RECEIVED_TPA_LIST_IS_EMPTY.show(sender);
return true;
}
BallAPI.getInstance().sendPlayerToPlayer(list, uuid,
Message.TPA_SUCCESS.getMessage(),
Message.TPA_ACCEPT_SUCCESS.getMessage());
DataManager.clearTPAList(uuid, true);
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,71 @@
package cn.hamster3.mc.plugin.tpa.command;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.core.bukkit.command.ParentCommand;
import cn.hamster3.mc.plugin.core.bukkit.constant.CoreMessage;
import cn.hamster3.mc.plugin.tpa.HamsterTPAPlugin;
import cn.hamster3.mc.plugin.tpa.config.ConfigManager;
import cn.hamster3.mc.plugin.tpa.core.DataManager;
import cn.hamster3.mc.plugin.tpa.core.Message;
import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public final class TPDenyCommand extends ParentCommand {
public static final TPDenyCommand INSTANCE = new TPDenyCommand();
private TPDenyCommand() {
super("hamster-tpdeny");
}
@Override
public @NotNull JavaPlugin getPlugin() {
return HamsterTPAPlugin.getInstance();
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!(sender instanceof Player)) {
CoreMessage.COMMAND_MUST_USED_BY_PLAYER.show(sender);
return true;
}
Player player = (Player) sender;
UUID uuid = player.getUniqueId();
long now = System.currentTimeMillis();
List<UUID> list = DataManager.getPlayerReceivedTpa(uuid).entrySet()
.stream()
.filter(o -> o.getValue() + ConfigManager.getTimeout() >= now)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (list.isEmpty()) {
Message.RECEIVED_TPA_LIST_IS_EMPTY.show(sender);
return true;
}
for (UUID requireUUID : list) {
DataManager.setPlayerUseTime(requireUUID, 0, true);
BallAPI.getInstance().sendMessageToPlayer(requireUUID,
Message.TPA_DENIED.getMessage().replace(TextReplacementConfig.builder()
.matchLiteral("%player_name%")
.replacement(player.getName())
.build()),
false);
}
DataManager.clearTPAList(uuid, true);
Message.TPA_DENY_SUCCESS.show(player);
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,33 @@
package cn.hamster3.mc.plugin.tpa.config;
import cn.hamster3.mc.plugin.tpa.HamsterTPAPlugin;
import cn.hamster3.mc.plugin.tpa.core.Message;
import org.bukkit.configuration.file.FileConfiguration;
public final class ConfigManager {
private static long tpaCD;
private static long timeout;
private ConfigManager() {
}
public static void init() {
HamsterTPAPlugin plugin = HamsterTPAPlugin.getInstance();
plugin.saveDefaultConfig();
plugin.reloadConfig();
FileConfiguration pluginConfig = plugin.getConfig();
tpaCD = pluginConfig.getLong("tpa-cd") * 1000L;
timeout = pluginConfig.getLong("timeout") * 1000L;
Message.init(plugin);
}
public static long getTpaCD() {
return tpaCD;
}
public static long getTimeout() {
return timeout;
}
}

View File

@@ -0,0 +1,57 @@
package cn.hamster3.mc.plugin.tpa.core;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.tpa.HamsterTPAPlugin;
import com.google.gson.JsonObject;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class DataManager {
private static Map<UUID, Long> PLAYER_USE_TIME;
private static Map<UUID, Map<UUID, Long>> PLAYER_RECEIVED_TPA;
public static void init() {
PLAYER_USE_TIME = new HashMap<>();
PLAYER_RECEIVED_TPA = new HashMap<>();
}
public static void setPlayerUseTime(@NotNull UUID uuid, long time, boolean broadcast) {
PLAYER_USE_TIME.put(uuid, time);
if (broadcast) {
JsonObject object = new JsonObject();
object.addProperty("uuid", uuid.toString());
object.addProperty("time", time);
BallAPI.getInstance().sendBallMessage(HamsterTPAPlugin.BALL_CHANNEL, "setPlayerUseTime", object);
}
}
public static void sendTPA(@NotNull UUID sender, @NotNull UUID target, long time, boolean broadcast) {
Map<UUID, Long> map = PLAYER_RECEIVED_TPA.computeIfAbsent(target, o -> new HashMap<>());
map.put(sender, time);
if (broadcast) {
JsonObject object = new JsonObject();
object.addProperty("sender", sender.toString());
object.addProperty("target", target.toString());
object.addProperty("time", time);
BallAPI.getInstance().sendBallMessage(HamsterTPAPlugin.BALL_CHANNEL, "sendTPA", object);
}
}
public static void clearTPAList(@NotNull UUID uuid, boolean broadcast) {
PLAYER_RECEIVED_TPA.remove(uuid);
if (broadcast) {
BallAPI.getInstance().sendBallMessage(HamsterTPAPlugin.BALL_CHANNEL, "clearTPAList", uuid.toString());
}
}
public static long getPlayerUseTime(@NotNull UUID uuid) {
return PLAYER_USE_TIME.getOrDefault(uuid, 0L);
}
public static Map<UUID, Long> getPlayerReceivedTpa(@NotNull UUID uuid) {
return PLAYER_RECEIVED_TPA.computeIfAbsent(uuid, o -> new HashMap<>());
}
}

View File

@@ -0,0 +1,62 @@
package cn.hamster3.mc.plugin.tpa.core;
import cn.hamster3.mc.plugin.core.bukkit.api.CoreBukkitAPI;
import cn.hamster3.mc.plugin.core.bukkit.util.CoreBukkitUtils;
import cn.hamster3.mc.plugin.core.common.data.DisplayMessage;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public enum Message {
TPA_IN_CD,
TPA_NOT_INPUT_PLAYER_NAME,
TPA_TARGET_PLAYER_NOT_ONLINE,
TPA_SEND_SUCCESS,
RECEIVED_TPA_LIST_IS_EMPTY,
TPA_RECEIVED,
TPA_SUCCESS,
TPA_ACCEPT_SUCCESS,
TPA_DENIED,
TPA_DENY_SUCCESS,
SEND_TPA_TIMEOUT,
RECEIVED_TPA_TIMEOUT;
private DisplayMessage message;
public static void init(@NotNull Plugin plugin) {
ConfigurationSection config = plugin.getConfig().getConfigurationSection("messages");
if (config == null) {
plugin.getLogger().warning("加载消息失败: 配置文件中未找到 messages 节点!");
return;
}
for (Message value : values()) {
ConfigurationSection section = config.getConfigurationSection(value.name());
if (section == null) {
continue;
}
try {
value.message = CoreBukkitUtils.getDisplayMessage(section);
} catch (Exception e) {
plugin.getLogger().warning("加载消息设置 " + value.name() + " 时遇到了一个异常: ");
e.printStackTrace();
}
}
}
public void show(CommandSender sender, TextReplacementConfig... replacement) {
if (message == null) {
sender.sendMessage(name());
return;
}
Audience audience = CoreBukkitAPI.getInstance().getAudienceProvider().sender(sender);
message.show(audience, replacement);
}
@SuppressWarnings("unused")
public DisplayMessage getMessage() {
return message;
}
}

View File

@@ -0,0 +1,52 @@
package cn.hamster3.mc.plugin.tpa.listener;
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
import cn.hamster3.mc.plugin.ball.common.listener.BallListener;
import cn.hamster3.mc.plugin.tpa.HamsterTPAPlugin;
import cn.hamster3.mc.plugin.tpa.core.DataManager;
import com.google.gson.JsonObject;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public final class MainListener implements BallListener {
public static final MainListener INSTANCE = new MainListener();
private MainListener() {
}
@Override
public void onMessageReceived(@NotNull BallMessageInfo event) {
if (!HamsterTPAPlugin.BALL_CHANNEL.equals(event.getChannel())) {
return;
}
switch (event.getAction()) {
case "setPlayerUseTime": {
JsonObject object = event.getContentAsJsonObject();
DataManager.setPlayerUseTime(
UUID.fromString(object.get("uuid").getAsString()),
object.get("time").getAsLong(),
false
);
break;
}
case "sendTPA": {
JsonObject object = event.getContentAsJsonObject();
DataManager.sendTPA(
UUID.fromString(object.get("sender").getAsString()),
UUID.fromString(object.get("target").getAsString()),
object.get("time").getAsLong(),
false
);
break;
}
case "clearTPAList": {
DataManager.clearTPAList(event.getContentAsUUID(), false);
break;
}
case "": {
break;
}
}
}
}

View File

@@ -0,0 +1,41 @@
package cn.hamster3.mc.plugin.tpa.thread;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.tpa.core.DataManager;
import cn.hamster3.mc.plugin.tpa.core.Message;
import net.kyori.adventure.text.TextReplacementConfig;
import java.util.Map;
import java.util.UUID;
public class TimeoutThread implements Runnable {
private final UUID sender;
private final UUID target;
public TimeoutThread(UUID sender, UUID target) {
this.sender = sender;
this.target = target;
}
@Override
public void run() {
Map<UUID, Long> map = DataManager.getPlayerReceivedTpa(target);
if (map.containsKey(sender)) {
BallAPI.getInstance().sendMessageToPlayer(sender,
Message.SEND_TPA_TIMEOUT.getMessage().replace(
TextReplacementConfig.builder()
.matchLiteral("%player_name%")
.replacement(BallAPI.getInstance().getPlayerName(target, ""))
.build()),
false);
BallAPI.getInstance().sendMessageToPlayer(target,
Message.RECEIVED_TPA_TIMEOUT.getMessage().replace(
TextReplacementConfig.builder()
.matchLiteral("%player_name%")
.replacement(BallAPI.getInstance().getPlayerName(sender, ""))
.build()),
false);
DataManager.setPlayerUseTime(sender, 0, true);
}
}
}

View File

@@ -0,0 +1,33 @@
# 使用 TPA 传送的冷却时间
# 以秒为单位
tpa-cd: 300
# 传送请求超时的时间
# 以秒为单位
timeout: 30
messages:
TPA_IN_CD:
message: "§a传送正在冷却中时间%cd_time% 秒"
TPA_NOT_INPUT_PLAYER_NAME:
message: "§c请输入要传送的玩家名称"
TPA_TARGET_PLAYER_NOT_ONLINE:
message: "§c该玩家不在线"
TPA_SEND_SUCCESS:
message: "§a已向玩家 %player_name% 发出传送申请"
RECEIVED_TPA_LIST_IS_EMPTY:
message: "§a你没有收到任何传送请求"
TPA_RECEIVED:
message: "§a收到了来自 %player_name% 的传送申请\n §a请输入 §e§l/tpaccept §a同意请求\n §a或使用 §c§l/tpdeny §a拒绝请求"
TPA_SUCCESS:
message: "§a已成功传送至玩家 %player_name%"
TPA_ACCEPT_SUCCESS:
message: "§a已成功接受玩家 %player_name% 的传送请求"
TPA_DENIED:
message: "§a玩家 %player_name% 拒绝了你的传送请求,冷却时间已返还"
TPA_DENY_SUCCESS:
message: "§a已拒绝玩家 %player_name% 的传送请求"
SEND_TPA_TIMEOUT:
message: "§a玩家 %player_name% 未能及时处理你的传送请求,冷却时间已返还"
RECEIVED_TPA_TIMEOUT:
message: "§a你未能及时处理 %player_name% 的传送请求"

View File

@@ -0,0 +1,25 @@
name: HamsterTPA
main: cn.hamster3.mc.plugin.tpa.HamsterTPAPlugin
version: ${version}
api-version: 1.13
author: MiniDay
website: https://github.com/MiniDay/hamster-little-plugins
description: TPA 功能
depend:
- HamsterCore
- HamsterBall
commands:
hamster-tpa:
aliases: [ tpa ]
hamster-tpaccept:
aliases: [ tpaccept ]
hamster-tpdeny:
aliases: [ tpdeny ]
permissions:
hamster.tpa.use:
default: true
description: 允许使用 HamsterTPA 的权限

View File

@@ -8,4 +8,5 @@ include 'hamster-sudo'
include 'hamster-lobby'
include 'hamster-spawn'
include 'hamster-chain-break'
include 'hamster-tpa'