Backup before major refactor of AdminMode based on user feedback
This commit is contained in:
@@ -3,15 +3,23 @@ package org.blz.adminmode;
|
||||
import org.blz.adminmode.commands.AdminModeCommand;
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.blz.adminmode.dialog.AdminDialogService;
|
||||
import org.blz.adminmode.integrations.CoreProtectIntegration;
|
||||
import org.blz.adminmode.integrations.LuckPermsIntegration;
|
||||
import org.blz.adminmode.listeners.AdminModeListener;
|
||||
import org.blz.adminmode.listeners.GameModeChangeListener;
|
||||
import org.blz.adminmode.listeners.MobTargetListener;
|
||||
import org.blz.adminmode.listeners.PlayerDeathListener;
|
||||
import org.blz.adminmode.listeners.PlayerSessionListener;
|
||||
import org.blz.adminmode.moderation.ModeManager;
|
||||
import org.blz.adminmode.moderation.ModeSessionLogger;
|
||||
import org.blz.adminmode.session.SessionStorage;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class AdminMode extends JavaPlugin {
|
||||
|
||||
private static AdminMode instance;
|
||||
private ConfigManager configManager;
|
||||
private ModeManager modeManager;
|
||||
private AdminModeCommand adminModeCommand;
|
||||
private AdminDialogService dialogService;
|
||||
|
||||
@@ -21,29 +29,40 @@ public class AdminMode extends JavaPlugin {
|
||||
saveDefaultConfig();
|
||||
|
||||
configManager = new ConfigManager(this);
|
||||
adminModeCommand = new AdminModeCommand(this, configManager);
|
||||
SessionStorage sessionStorage = new SessionStorage(this);
|
||||
LuckPermsIntegration luckPermsIntegration = new LuckPermsIntegration(this, configManager);
|
||||
CoreProtectIntegration coreProtectIntegration = new CoreProtectIntegration(this, configManager);
|
||||
ModeSessionLogger sessionLogger = new ModeSessionLogger(this, configManager);
|
||||
modeManager = new ModeManager(this, configManager, luckPermsIntegration, sessionStorage, sessionLogger);
|
||||
adminModeCommand = new AdminModeCommand(configManager, modeManager, coreProtectIntegration);
|
||||
dialogService = new AdminDialogService(this, configManager, adminModeCommand);
|
||||
adminModeCommand.setDialogService(dialogService);
|
||||
getCommand("adminmode").setExecutor(adminModeCommand);
|
||||
getCommand("admfix").setExecutor(new org.blz.adminmode.commands.AdminFixCommand(adminModeCommand));
|
||||
|
||||
getServer().getPluginManager().registerEvents(new AdminModeListener(this, adminModeCommand, configManager),
|
||||
getServer().getPluginManager().registerEvents(new AdminModeListener(adminModeCommand, configManager, modeManager, dialogService),
|
||||
this);
|
||||
getServer().getPluginManager().registerEvents(new MobTargetListener(adminModeCommand, configManager), this);
|
||||
getServer().getPluginManager().registerEvents(new GameModeChangeListener(adminModeCommand, configManager),
|
||||
this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerSessionListener(adminModeCommand, modeManager), this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerDeathListener(modeManager), this);
|
||||
|
||||
for (org.bukkit.entity.Player onlinePlayer : getServer().getOnlinePlayers()) {
|
||||
if (modeManager.hasSession(onlinePlayer.getUniqueId())) {
|
||||
getServer().getScheduler().runTaskLater(this, () -> adminModeCommand.recoverSession(onlinePlayer), 1L);
|
||||
}
|
||||
}
|
||||
|
||||
// Load Pocket Dimension World
|
||||
new org.blz.adminmode.utils.WorldLoader(this, "pocket_dimension").loadWorld();
|
||||
|
||||
// Register Abilities
|
||||
belzeManager = new org.blz.adminmode.managers.BelzeBoolManager(this);
|
||||
new org.blz.adminmode.managers.BelzeBoolManager(this);
|
||||
|
||||
getLogger().info("AdminMode успешно включен!");
|
||||
}
|
||||
|
||||
private org.blz.adminmode.managers.BelzeBoolManager belzeManager; // Field to store manager
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getLogger().info("AdminMode отключен!");
|
||||
@@ -57,6 +76,10 @@ public class AdminMode extends JavaPlugin {
|
||||
return configManager;
|
||||
}
|
||||
|
||||
public ModeManager getModeManager() {
|
||||
return modeManager;
|
||||
}
|
||||
|
||||
public AdminModeCommand getAdminModeCommand() {
|
||||
return adminModeCommand;
|
||||
}
|
||||
|
||||
@@ -1,47 +1,36 @@
|
||||
package org.blz.adminmode.commands;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.model.user.User;
|
||||
import net.luckperms.api.node.Node;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.blz.adminmode.dialog.AdminDialogService;
|
||||
import org.blz.adminmode.integrations.CoreProtectIntegration;
|
||||
import org.blz.adminmode.moderation.ModeManager;
|
||||
import org.blz.adminmode.moderation.ModeratorProfile;
|
||||
import org.blz.adminmode.moderation.PlayerListAction;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.blz.adminmode.dialog.AdminDialogService;
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.blz.adminmode.data.AdminModeDataManager;
|
||||
import org.blz.adminmode.data.AdminModeDataManager.PlayerStateData;
|
||||
import org.blz.adminmode.moderation.ModeratorProfile;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public class AdminModeCommand implements CommandExecutor {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final ConfigManager configManager;
|
||||
private final Map<UUID, PlayerState> savedStates = new HashMap<>();
|
||||
private final Map<UUID, ModeratorProfile> activeProfiles = new HashMap<>();
|
||||
private final AdminModeDataManager dataManager;
|
||||
private final ModeManager modeManager;
|
||||
private final CoreProtectIntegration coreProtectIntegration;
|
||||
private AdminDialogService dialogService;
|
||||
|
||||
public AdminModeCommand(Plugin plugin, ConfigManager configManager) {
|
||||
this.plugin = plugin;
|
||||
public AdminModeCommand(
|
||||
ConfigManager configManager,
|
||||
ModeManager modeManager,
|
||||
CoreProtectIntegration coreProtectIntegration) {
|
||||
this.configManager = configManager;
|
||||
this.dataManager = new AdminModeDataManager(plugin);
|
||||
loadAllStates();
|
||||
this.modeManager = modeManager;
|
||||
this.coreProtectIntegration = coreProtectIntegration;
|
||||
}
|
||||
|
||||
public void setDialogService(AdminDialogService dialogService) {
|
||||
@@ -52,23 +41,22 @@ public class AdminModeCommand implements CommandExecutor {
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("reload")) {
|
||||
if (!sender.hasPermission("adminmode.reload")) {
|
||||
sender.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
|
||||
sender.sendMessage(configManager.getPrefixedMessage(configManager.getMsgNoPermission()));
|
||||
return true;
|
||||
}
|
||||
configManager.loadConfig();
|
||||
sender.sendMessage(ChatColor.GREEN + configManager.getMsgReloaded());
|
||||
sender.sendMessage(configManager.getPrefixedMessage(configManager.getMsgReloaded()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Команда disable <игрок> - принудительно отключить способности
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("disable")) {
|
||||
if (!canUseHardActions(sender)) {
|
||||
sender.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
|
||||
sender.sendMessage(configManager.getPrefixedMessage(configManager.getMsgNoPermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(ChatColor.RED + "Использование: /adminmode disable <игрок>");
|
||||
sender.sendMessage(configManager.getPrefixedMessage("§cИспользование: /adminmode disable <игрок>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -76,132 +64,121 @@ public class AdminModeCommand implements CommandExecutor {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(ChatColor.RED + "Эта команда доступна только игрокам!");
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Эта команда доступна только игрокам!");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
|
||||
if (!configManager.isEnabled()) {
|
||||
player.sendMessage(ChatColor.RED + "Режим администратора отключен!");
|
||||
player.sendMessage(configManager.getPrefixedMessage("§cРежим администратора отключен!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("dialog")) {
|
||||
if (!openRelevantDialog(player)) {
|
||||
player.sendMessage(ChatColor.YELLOW + "Dialog-панель недоступна. Используйте /adminmode enable <soft|hard>.");
|
||||
player.sendMessage(configManager.getPrefixedMessage("§eDialog-панель сейчас недоступна."));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0 && (args[0].equalsIgnoreCase("enable") || args[0].equalsIgnoreCase("profile"))) {
|
||||
if (args.length < 2) {
|
||||
if (!openRelevantDialog(player)) {
|
||||
player.sendMessage(ChatColor.YELLOW + "Использование: /adminmode enable <soft|hard>");
|
||||
}
|
||||
openRelevantDialog(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
ModeratorProfile profile = ModeratorProfile.fromInput(args[1]).orElse(null);
|
||||
if (profile == null) {
|
||||
player.sendMessage(ChatColor.RED + "Неизвестный профиль. Доступно: soft, hard.");
|
||||
player.sendMessage(configManager.getPrefixedMessage("§cНеизвестный профиль. Доступно: soft, hard."));
|
||||
return true;
|
||||
}
|
||||
|
||||
toggleAdminMode(player, profile);
|
||||
activateProfile(player, profile);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isInAdminMode(player.getUniqueId()) && getAvailableProfiles(player).isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgNoPermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
toggleAdminMode(player);
|
||||
openRelevantDialog(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void toggleAdminMode(Player player) {
|
||||
if (isInAdminMode(player.getUniqueId())) {
|
||||
disableAdminMode(player);
|
||||
return;
|
||||
}
|
||||
|
||||
List<ModeratorProfile> availableProfiles = getAvailableProfiles(player);
|
||||
if (availableProfiles.isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
|
||||
return;
|
||||
}
|
||||
|
||||
if (availableProfiles.size() == 1) {
|
||||
enableAdminMode(player, availableProfiles.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!openRelevantDialog(player)) {
|
||||
player.sendMessage(ChatColor.YELLOW + "Доступно несколько профилей. Используйте /adminmode enable <soft|hard>.");
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleAdminMode(Player player, ModeratorProfile profile) {
|
||||
public boolean activateProfile(Player player, ModeratorProfile profile) {
|
||||
if (profile == null) {
|
||||
toggleAdminMode(player);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInAdminMode(player.getUniqueId())) {
|
||||
ModeratorProfile activeProfile = getActiveProfile(player.getUniqueId());
|
||||
if (activeProfile == profile) {
|
||||
disableAdminMode(player);
|
||||
} else {
|
||||
player.sendMessage(ChatColor.YELLOW + "Сначала выключите текущий режим модерации, затем включите новый профиль.");
|
||||
}
|
||||
return;
|
||||
return openRelevantDialog(player);
|
||||
}
|
||||
|
||||
if (!profile.isAvailableFor(player)) {
|
||||
player.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
|
||||
return;
|
||||
}
|
||||
|
||||
enableAdminMode(player, profile);
|
||||
}
|
||||
|
||||
public List<ModeratorProfile> getAvailableProfiles(Player player) {
|
||||
return ModeratorProfile.availableFor(player);
|
||||
}
|
||||
|
||||
public ModeratorProfile getActiveProfile(UUID playerId) {
|
||||
return activeProfiles.get(playerId);
|
||||
}
|
||||
|
||||
public void applyAdminSettings(Player player, float flySpeed, float walkSpeed, boolean godMode) {
|
||||
if (!isInAdminMode(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.setAllowFlight(true);
|
||||
player.setFlying(true);
|
||||
player.setFlySpeed(clampSpeed(flySpeed, 0.1f));
|
||||
player.setWalkSpeed(clampSpeed(walkSpeed, 0.1f));
|
||||
player.setInvulnerable(godMode);
|
||||
player.sendMessage(ChatColor.GREEN + "✓ Настройки admin mode обновлены.");
|
||||
}
|
||||
|
||||
public boolean teleportToPlayer(Player moderator, String targetName) {
|
||||
if (!validateHardActionPlayer(moderator)) {
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgNoPermission()));
|
||||
return false;
|
||||
}
|
||||
|
||||
Player target = resolveOnlinePlayer(targetName, moderator);
|
||||
if (modeManager.hasSession(player.getUniqueId())) {
|
||||
ModeratorProfile activeProfile = getActiveProfile(player.getUniqueId());
|
||||
if (activeProfile == profile) {
|
||||
return openRelevantDialog(player);
|
||||
}
|
||||
|
||||
player.sendMessage(configManager.getPrefixedMessage(
|
||||
"§eУ вас уже активен профиль §f" + activeProfile.getDisplayName() + "§e. Сначала выйдите из режима."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return modeManager.enterMode(player, profile);
|
||||
}
|
||||
|
||||
public boolean exitMode(Player player) {
|
||||
return modeManager.exitMode(player);
|
||||
}
|
||||
|
||||
public boolean recoverSession(Player player) {
|
||||
return modeManager.recoverSession(player);
|
||||
}
|
||||
|
||||
public List<ModeratorProfile> getAvailableProfiles(Player player) {
|
||||
return modeManager.getAvailableProfiles(player);
|
||||
}
|
||||
|
||||
public ModeratorProfile getActiveProfile(UUID playerId) {
|
||||
return modeManager.getProfile(playerId);
|
||||
}
|
||||
|
||||
public boolean isInAdminMode(UUID playerId) {
|
||||
return modeManager.hasSession(playerId);
|
||||
}
|
||||
|
||||
public List<Component> getOnlineSummary(Player player) {
|
||||
return modeManager.buildOnlineSummary(player);
|
||||
}
|
||||
|
||||
public void applyAdminSettings(Player player, float flySpeed, float walkSpeed, boolean godMode) {
|
||||
if (!modeManager.hasSession(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
modeManager.applyAdminSettings(player, flySpeed, walkSpeed, godMode);
|
||||
player.sendMessage(configManager.getPrefixedMessage("§aНастройки режима обновлены."));
|
||||
}
|
||||
|
||||
public boolean performPlayerListAction(Player moderator, PlayerListAction action, String targetName) {
|
||||
Player target = validateActiveTarget(moderator, targetName);
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
moderator.teleport(target.getLocation());
|
||||
moderator.sendMessage(ChatColor.GREEN + "✓ Вы телепортированы к игроку " + target.getName());
|
||||
return true;
|
||||
return switch (action) {
|
||||
case SPECTATE -> modeManager.spectatePlayer(moderator, target);
|
||||
case TELEPORT -> modeManager.teleportToPlayer(moderator, target);
|
||||
case INVSEE -> modeManager.openInvsee(moderator, target);
|
||||
};
|
||||
}
|
||||
|
||||
public boolean teleportToPlayer(Player moderator, String targetName) {
|
||||
Player target = validateActiveTarget(moderator, targetName);
|
||||
return target != null && modeManager.teleportToPlayer(moderator, target);
|
||||
}
|
||||
|
||||
public boolean teleportPlayerHere(Player moderator, String targetName) {
|
||||
@@ -210,19 +187,12 @@ public class AdminModeCommand implements CommandExecutor {
|
||||
}
|
||||
|
||||
Player target = resolveOnlinePlayer(targetName, moderator);
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target.teleport(moderator.getLocation());
|
||||
moderator.sendMessage(ChatColor.GREEN + "✓ Игрок " + target.getName() + " телепортирован к вам.");
|
||||
target.sendMessage(ChatColor.YELLOW + "⚠ Вас телепортировал модератор " + moderator.getName());
|
||||
return true;
|
||||
return target != null && modeManager.teleportPlayerHere(moderator, target);
|
||||
}
|
||||
|
||||
public boolean forceDisableTarget(CommandSender sender, String targetName) {
|
||||
if (!canUseHardActions(sender)) {
|
||||
sender.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
|
||||
sender.sendMessage(configManager.getPrefixedMessage(configManager.getMsgNoPermission()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -232,296 +202,81 @@ public class AdminModeCommand implements CommandExecutor {
|
||||
}
|
||||
|
||||
forceDisableAbilities(target);
|
||||
sender.sendMessage(ChatColor.GREEN + "✓ Все способности отключены для игрока " + target.getName());
|
||||
target.sendMessage(ChatColor.YELLOW + "⚠ Ваши способности были сброшены модератором");
|
||||
sender.sendMessage(configManager.getPrefixedMessage("§aРежим и способности игрока §f" + target.getName() + "§a сброшены."));
|
||||
target.sendMessage(configManager.getPrefixedMessage("§eВаш режим модерации был принудительно отключен."));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean executeCPLookup(Player player, String targetPlayer, int radius, String time) {
|
||||
if (!validateHardActionPlayer(player)) {
|
||||
return false;
|
||||
}
|
||||
return coreProtectIntegration.lookup(player, targetPlayer, radius, time);
|
||||
}
|
||||
|
||||
public boolean executeCPRollback(Player player, int radius, String time, String targetPlayer) {
|
||||
if (!validateHardActionPlayer(player)) {
|
||||
return false;
|
||||
}
|
||||
return coreProtectIntegration.rollback(player, radius, time, targetPlayer);
|
||||
}
|
||||
|
||||
public boolean toggleInspector(Player player) {
|
||||
if (!validateHardActionPlayer(player)) {
|
||||
return false;
|
||||
}
|
||||
return coreProtectIntegration.toggleInspector(player);
|
||||
}
|
||||
|
||||
public void forceDisableAbilities(Player player) {
|
||||
if (modeManager.hasSession(player.getUniqueId())) {
|
||||
modeManager.exitMode(player);
|
||||
return;
|
||||
}
|
||||
|
||||
player.setInvulnerable(false);
|
||||
player.setFlying(false);
|
||||
player.setAllowFlight(false);
|
||||
player.setFlySpeed(0.1f);
|
||||
player.setWalkSpeed(0.2f);
|
||||
}
|
||||
|
||||
public boolean canUseHardActions(CommandSender sender) {
|
||||
return modeManager.canUseHardActions(sender);
|
||||
}
|
||||
|
||||
private boolean openRelevantDialog(Player player) {
|
||||
if (dialogService == null || !configManager.isDialogsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isInAdminMode(player.getUniqueId())) {
|
||||
return dialogService.openModerationPanel(player);
|
||||
if (modeManager.hasSession(player.getUniqueId())) {
|
||||
return dialogService.openCurrentPanel(player);
|
||||
}
|
||||
|
||||
return dialogService.openProfileSelectionDialog(player);
|
||||
}
|
||||
|
||||
private void enableAdminMode(Player player, ModeratorProfile profile) {
|
||||
savePlayerState(player, profile);
|
||||
activeProfiles.put(player.getUniqueId(), profile);
|
||||
clearPlayerState(player);
|
||||
givePresetBlocks(player);
|
||||
setAdminGameMode(player);
|
||||
savePlayerStateToFile(player);
|
||||
grantLuckPermsPermissions(player, profile);
|
||||
|
||||
player.sendMessage(ChatColor.AQUA + configManager.getMsgEnabled());
|
||||
player.sendMessage(ChatColor.GRAY + configManager.getMsgEnabledSubtitle());
|
||||
player.sendMessage(ChatColor.YELLOW + configManager.getMsgPermissionsGranted());
|
||||
player.sendMessage(ChatColor.GRAY + "Профиль: " + profile.getDisplayName());
|
||||
}
|
||||
|
||||
private void disableAdminMode(Player player) {
|
||||
UUID playerId = player.getUniqueId();
|
||||
ModeratorProfile profile = activeProfiles.getOrDefault(playerId, ModeratorProfile.SOFT_MODER);
|
||||
|
||||
removeLuckPermsPermissions(player, profile);
|
||||
restorePlayerState(player);
|
||||
dataManager.deletePlayerState(playerId);
|
||||
|
||||
player.sendMessage(ChatColor.GREEN + configManager.getMsgDisabled());
|
||||
player.sendMessage(ChatColor.GRAY + configManager.getMsgDisabledSubtitle());
|
||||
}
|
||||
|
||||
private void savePlayerState(Player player, ModeratorProfile profile) {
|
||||
PlayerState state = new PlayerState();
|
||||
|
||||
state.inventory = player.getInventory().getContents().clone();
|
||||
state.armorContents = player.getInventory().getArmorContents().clone();
|
||||
state.offHand = player.getInventory().getItemInOffHand().clone();
|
||||
|
||||
state.location = player.getLocation().clone();
|
||||
|
||||
state.health = player.getHealth();
|
||||
state.foodLevel = player.getFoodLevel();
|
||||
state.saturation = player.getSaturation();
|
||||
state.exhaustion = player.getExhaustion();
|
||||
|
||||
state.exp = player.getExp();
|
||||
state.level = player.getLevel();
|
||||
state.totalExperience = player.getTotalExperience();
|
||||
state.gameMode = player.getGameMode();
|
||||
|
||||
state.allowFlight = player.getAllowFlight();
|
||||
state.flying = player.isFlying();
|
||||
state.flySpeed = player.getFlySpeed();
|
||||
state.walkSpeed = player.getWalkSpeed();
|
||||
state.potionEffects = player.getActivePotionEffects();
|
||||
state.fireTicks = player.getFireTicks();
|
||||
state.profile = profile;
|
||||
|
||||
savedStates.put(player.getUniqueId(), state);
|
||||
}
|
||||
|
||||
private void clearPlayerState(Player player) {
|
||||
player.getInventory().clear();
|
||||
player.getInventory().setArmorContents(new ItemStack[4]);
|
||||
player.getInventory().setItemInOffHand(new ItemStack(Material.AIR));
|
||||
|
||||
player.setHealth(20.0);
|
||||
player.setFoodLevel(20);
|
||||
player.setSaturation(5.0f);
|
||||
player.setExhaustion(0.0f);
|
||||
|
||||
player.setExp(0);
|
||||
player.setLevel(0);
|
||||
player.setTotalExperience(0);
|
||||
|
||||
for (PotionEffect effect : player.getActivePotionEffects()) {
|
||||
player.removePotionEffect(effect.getType());
|
||||
}
|
||||
|
||||
player.setFireTicks(0);
|
||||
}
|
||||
|
||||
private void givePresetBlocks(Player player) {
|
||||
if (!configManager.isGivePresetBlocks())
|
||||
return;
|
||||
|
||||
List<String> presetBlocks = configManager.getPresetBlocks();
|
||||
for (String blockStr : presetBlocks) {
|
||||
try {
|
||||
String[] parts = blockStr.split(":");
|
||||
Material material = Material.valueOf(parts[0].toUpperCase());
|
||||
int amount = parts.length > 1 ? Integer.parseInt(parts[1]) : 64;
|
||||
|
||||
ItemStack item = new ItemStack(material, amount);
|
||||
player.getInventory().addItem(item);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Неверный блок в preset_blocks: " + blockStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setAdminGameMode(Player player) {
|
||||
if (configManager.isForceSpectator()) {
|
||||
player.setGameMode(GameMode.SPECTATOR);
|
||||
} else {
|
||||
// ВАЖНО: Не используем CREATIVE для модеров, только SURVIVAL!
|
||||
player.setGameMode(GameMode.SURVIVAL);
|
||||
}
|
||||
// Всегда разрешаем полет в админ моде (даже в выживании)
|
||||
player.setAllowFlight(true);
|
||||
player.setFlying(true);
|
||||
|
||||
// Дополнительные возможности
|
||||
if (configManager.isGodMode()) {
|
||||
player.setInvulnerable(true);
|
||||
}
|
||||
if (configManager.getCustomFlySpeed() > 0) {
|
||||
player.setFlySpeed(configManager.getCustomFlySpeed());
|
||||
}
|
||||
if (configManager.getCustomWalkSpeed() > 0) {
|
||||
player.setWalkSpeed(configManager.getCustomWalkSpeed());
|
||||
}
|
||||
}
|
||||
|
||||
private void savePlayerStateToFile(Player player) {
|
||||
UUID playerId = player.getUniqueId();
|
||||
PlayerState state = savedStates.get(playerId);
|
||||
|
||||
if (state == null)
|
||||
return;
|
||||
|
||||
PlayerStateData data = new PlayerStateData();
|
||||
data.inventory = state.inventory;
|
||||
data.armorContents = state.armorContents;
|
||||
data.offHand = state.offHand;
|
||||
data.location = state.location;
|
||||
data.health = state.health;
|
||||
data.foodLevel = state.foodLevel;
|
||||
data.saturation = state.saturation;
|
||||
data.exhaustion = state.exhaustion;
|
||||
data.exp = state.exp;
|
||||
data.level = state.level;
|
||||
data.totalExperience = state.totalExperience;
|
||||
data.gameMode = state.gameMode;
|
||||
data.allowFlight = state.allowFlight;
|
||||
data.flying = state.flying;
|
||||
data.flySpeed = state.flySpeed;
|
||||
data.walkSpeed = state.walkSpeed;
|
||||
data.potionEffects = state.potionEffects;
|
||||
data.fireTicks = state.fireTicks;
|
||||
data.profileKey = state.profile == null ? null : state.profile.getConfigKey();
|
||||
|
||||
dataManager.savePlayerState(playerId, data);
|
||||
}
|
||||
|
||||
private void loadAllStates() {
|
||||
for (UUID playerId : dataManager.getAllSavedStates()) {
|
||||
PlayerStateData data = dataManager.loadPlayerState(playerId);
|
||||
|
||||
if (data == null)
|
||||
continue;
|
||||
|
||||
PlayerState state = new PlayerState();
|
||||
state.inventory = data.inventory;
|
||||
state.armorContents = data.armorContents;
|
||||
state.offHand = data.offHand;
|
||||
state.location = data.location;
|
||||
state.health = data.health;
|
||||
state.foodLevel = data.foodLevel;
|
||||
state.saturation = data.saturation;
|
||||
state.exhaustion = data.exhaustion;
|
||||
state.exp = data.exp;
|
||||
state.level = data.level;
|
||||
state.totalExperience = data.totalExperience;
|
||||
state.gameMode = data.gameMode;
|
||||
state.allowFlight = data.allowFlight;
|
||||
state.flying = data.flying;
|
||||
state.flySpeed = data.flySpeed;
|
||||
state.walkSpeed = data.walkSpeed;
|
||||
state.potionEffects = data.potionEffects;
|
||||
state.fireTicks = data.fireTicks;
|
||||
state.profile = ModeratorProfile.fromConfigKey(data.profileKey).orElse(ModeratorProfile.SOFT_MODER);
|
||||
|
||||
savedStates.put(playerId, state);
|
||||
activeProfiles.put(playerId, state.profile);
|
||||
plugin.getLogger().info("Загружено состояние админ мода для: " + playerId);
|
||||
}
|
||||
}
|
||||
|
||||
private void restorePlayerState(Player player) {
|
||||
UUID playerId = player.getUniqueId();
|
||||
PlayerState state = savedStates.get(playerId);
|
||||
|
||||
if (state == null)
|
||||
return;
|
||||
|
||||
// КРИТИЧНО: Удаляем из savedStates ДО восстановления режима!
|
||||
// Это предотвратит срабатывание GameModeChangeListener, который мог бы снова
|
||||
// включить полет
|
||||
savedStates.remove(playerId);
|
||||
activeProfiles.remove(playerId);
|
||||
|
||||
// Восстанавливаем инвентарь
|
||||
player.getInventory().setContents(state.inventory);
|
||||
player.getInventory().setArmorContents(state.armorContents);
|
||||
player.getInventory().setItemInOffHand(state.offHand);
|
||||
|
||||
if (state.location != null) {
|
||||
player.teleport(state.location);
|
||||
}
|
||||
|
||||
player.setHealth(state.health);
|
||||
player.setFoodLevel(state.foodLevel);
|
||||
player.setSaturation(state.saturation);
|
||||
player.setExhaustion(state.exhaustion);
|
||||
|
||||
player.setExp(state.exp);
|
||||
player.setLevel(state.level);
|
||||
player.setTotalExperience(state.totalExperience);
|
||||
|
||||
// ВАЖНО: Сначала восстанавливаем gameMode
|
||||
player.setGameMode(state.gameMode);
|
||||
|
||||
// КРИТИЧНО: ПОТОМ применяем железную очистку способностей!
|
||||
// Это гарантирует, что даже в SPECTATOR режиме у игрока не будет полета
|
||||
forceDisableAbilities(player);
|
||||
|
||||
for (PotionEffect effect : player.getActivePotionEffects()) {
|
||||
player.removePotionEffect(effect.getType());
|
||||
}
|
||||
for (PotionEffect effect : state.potionEffects) {
|
||||
player.addPotionEffect(effect);
|
||||
}
|
||||
|
||||
player.setFireTicks(state.fireTicks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Железное отключение всех админ способностей
|
||||
* Используется при выходе из режима и в команде /admfix
|
||||
*/
|
||||
public void forceDisableAbilities(Player player) {
|
||||
player.setInvulnerable(false); // Убираем бессмертие
|
||||
player.setFlying(false); // Принудительно отключаем полет
|
||||
player.setAllowFlight(false); // Запрещаем полет
|
||||
player.setFlySpeed(0.1f); // Сброс скорости полета на дефолт
|
||||
player.setWalkSpeed(0.2f); // Сброс скорости ходьбы на дефолт
|
||||
}
|
||||
|
||||
public boolean isInAdminMode(UUID playerId) {
|
||||
return savedStates.containsKey(playerId);
|
||||
}
|
||||
|
||||
private boolean canUseHardActions(CommandSender sender) {
|
||||
if (sender.hasPermission("adminmode.admin")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isInAdminMode(player.getUniqueId()) && getActiveProfile(player.getUniqueId()) == ModeratorProfile.HARD_MODER;
|
||||
return dialogService.openModeSelectDialog(player);
|
||||
}
|
||||
|
||||
private boolean validateHardActionPlayer(Player moderator) {
|
||||
if (!canUseHardActions(moderator)) {
|
||||
moderator.sendMessage(ChatColor.RED + "Для этого действия нужен активный профиль Hard Moder.");
|
||||
moderator.sendMessage(configManager.getPrefixedMessage("§cДля этого действия нужен активный профиль Hard Moder."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Player validateActiveTarget(Player moderator, String targetName) {
|
||||
if (!modeManager.hasSession(moderator.getUniqueId())) {
|
||||
moderator.sendMessage(configManager.getPrefixedMessage("§cСначала войдите в режим модерации."));
|
||||
return null;
|
||||
}
|
||||
return resolveOnlinePlayer(targetName, moderator);
|
||||
}
|
||||
|
||||
private Player resolveOnlinePlayer(String targetName, CommandSender sender) {
|
||||
if (targetName == null || targetName.isBlank()) {
|
||||
sender.sendMessage(ChatColor.RED + "Укажите ник игрока.");
|
||||
sender.sendMessage(configManager.getPrefixedMessage("§cУкажите ник игрока."));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -531,102 +286,10 @@ public class AdminModeCommand implements CommandExecutor {
|
||||
}
|
||||
|
||||
if (target == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Игрок не найден!");
|
||||
sender.sendMessage(configManager.getPrefixedMessage("§cИгрок не найден!"));
|
||||
return null;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
private float clampSpeed(float value, float min) {
|
||||
return Math.max(min, Math.min(1.0f, value));
|
||||
}
|
||||
|
||||
private void grantLuckPermsPermissions(Player player, ModeratorProfile profile) {
|
||||
if (!configManager.isUseLuckPerms()) {
|
||||
player.setOp(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
LuckPerms luckPerms = LuckPermsProvider.get();
|
||||
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
|
||||
|
||||
if (user == null) {
|
||||
player.sendMessage(ChatColor.RED + "⚠ Не удалось получить данные LuckPerms!");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> groups = configManager.getLuckPermsGroups(profile);
|
||||
|
||||
for (String group : groups) {
|
||||
Node groupNode = Node.builder("group." + group).build();
|
||||
user.data().add(groupNode);
|
||||
}
|
||||
|
||||
luckPerms.getUserManager().saveUser(user);
|
||||
|
||||
player.sendMessage(ChatColor.GREEN + "✓ Права администратора выданы!");
|
||||
} catch (IllegalStateException e) {
|
||||
player.setOp(true);
|
||||
player.sendMessage(ChatColor.YELLOW + "⚠ LuckPerms не найден, используется стандартный OP");
|
||||
}
|
||||
}
|
||||
|
||||
private void removeLuckPermsPermissions(Player player, ModeratorProfile profile) {
|
||||
if (!configManager.isUseLuckPerms()) {
|
||||
player.setOp(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configManager.isRemoveGroupsOnDisable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
LuckPerms luckPerms = LuckPermsProvider.get();
|
||||
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
|
||||
|
||||
if (user == null) {
|
||||
player.sendMessage(ChatColor.RED + "⚠ Не удалось получить данные LuckPerms!");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> groups = configManager.getLuckPermsGroups(profile);
|
||||
|
||||
for (String group : groups) {
|
||||
Node groupNode = Node.builder("group." + group).build();
|
||||
user.data().remove(groupNode);
|
||||
}
|
||||
|
||||
luckPerms.getUserManager().saveUser(user);
|
||||
|
||||
player.sendMessage(ChatColor.GREEN + configManager.getMsgPermissionsRemoved());
|
||||
} catch (IllegalStateException e) {
|
||||
player.setOp(false);
|
||||
player.sendMessage(ChatColor.YELLOW + "⚠ LuckPerms не найден, убран стандартный OP");
|
||||
}
|
||||
}
|
||||
|
||||
private static class PlayerState {
|
||||
ItemStack[] inventory;
|
||||
ItemStack[] armorContents;
|
||||
ItemStack offHand;
|
||||
Location location;
|
||||
double health;
|
||||
int foodLevel;
|
||||
float saturation;
|
||||
float exhaustion;
|
||||
float exp;
|
||||
int level;
|
||||
int totalExperience;
|
||||
GameMode gameMode;
|
||||
boolean allowFlight;
|
||||
boolean flying;
|
||||
float flySpeed;
|
||||
float walkSpeed;
|
||||
Collection<PotionEffect> potionEffects;
|
||||
int fireTicks;
|
||||
ModeratorProfile profile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.blz.adminmode.moderation.ModeratorProfile;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@@ -19,6 +20,17 @@ public class ConfigManager {
|
||||
private Map<ModeratorProfile, List<String>> luckPermsGroups;
|
||||
private boolean removeGroupsOnDisable;
|
||||
private boolean dialogsEnabled;
|
||||
private String baseGroup;
|
||||
private String softGroup;
|
||||
private String hardGroup;
|
||||
private Material softPanelMaterial;
|
||||
private Material hardPanelMaterial;
|
||||
private Material cpInspectorMaterial;
|
||||
private boolean coreProtectEnabled;
|
||||
private int coreProtectDefaultRadius;
|
||||
private String coreProtectDefaultTime;
|
||||
private boolean sessionLoggingEnabled;
|
||||
private String sessionLogFile;
|
||||
|
||||
private boolean preventItemDrop;
|
||||
private boolean preventItemPickup;
|
||||
@@ -50,6 +62,12 @@ public class ConfigManager {
|
||||
private String msgItemUseDenied;
|
||||
private String msgBlockPlaceDenied;
|
||||
private String msgReloaded;
|
||||
private String msgPrefix;
|
||||
private String msgEnterSoft;
|
||||
private String msgEnterHard;
|
||||
private String msgExitMode;
|
||||
private String msgCoreProtectUnavailable;
|
||||
private String msgSessionRecovered;
|
||||
|
||||
public ConfigManager(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
@@ -66,14 +84,37 @@ public class ConfigManager {
|
||||
removeGroupsOnDisable = config.getBoolean("admin_mode.luckperms.remove_on_disable", true);
|
||||
dialogsEnabled = config.getBoolean("admin_mode.dialogs.enabled", true);
|
||||
|
||||
baseGroup = config.getString("groups.base", "moder");
|
||||
softGroup = config.getString("groups.soft", "soft_moder_adm");
|
||||
hardGroup = config.getString("groups.hard", "hard_moder_adm");
|
||||
|
||||
softPanelMaterial = readMaterial("items.soft_panel_material", Material.COMPASS);
|
||||
hardPanelMaterial = readMaterial("items.hard_panel_material", Material.NETHER_STAR);
|
||||
cpInspectorMaterial = readMaterial("items.cp_inspector_material", Material.STICK);
|
||||
|
||||
coreProtectEnabled = config.getBoolean("coreprotect.enabled", true);
|
||||
coreProtectDefaultRadius = config.getInt("coreprotect.default_radius", 10);
|
||||
coreProtectDefaultTime = config.getString("coreprotect.default_time", "24h");
|
||||
|
||||
sessionLoggingEnabled = config.getBoolean("logging.enabled", true);
|
||||
sessionLogFile = config.getString("logging.log_file", "logs/sessions.log");
|
||||
|
||||
List<String> legacyGroups = config.getStringList("admin_mode.luckperms.groups");
|
||||
List<String> softGroups = config.getStringList("admin_mode.luckperms.profile_groups.soft_moder");
|
||||
List<String> hardGroups = config.getStringList("admin_mode.luckperms.profile_groups.hard_moder");
|
||||
if (softGroups.isEmpty()) {
|
||||
softGroups = new ArrayList<>(legacyGroups);
|
||||
if (!legacyGroups.isEmpty()) {
|
||||
softGroups = new ArrayList<>(legacyGroups);
|
||||
} else {
|
||||
softGroups = new ArrayList<>(List.of(softGroup));
|
||||
}
|
||||
}
|
||||
if (hardGroups.isEmpty()) {
|
||||
hardGroups = legacyGroups.isEmpty() ? new ArrayList<>(softGroups) : new ArrayList<>(legacyGroups);
|
||||
if (!legacyGroups.isEmpty()) {
|
||||
hardGroups = new ArrayList<>(legacyGroups);
|
||||
} else {
|
||||
hardGroups = new ArrayList<>(List.of(hardGroup));
|
||||
}
|
||||
}
|
||||
luckPermsGroups = new EnumMap<>(ModeratorProfile.class);
|
||||
luckPermsGroups.put(ModeratorProfile.SOFT_MODER, new ArrayList<>(softGroups));
|
||||
@@ -117,6 +158,15 @@ public class ConfigManager {
|
||||
msgBlockPlaceDenied = config.getString("admin_mode.messages.block_place_denied",
|
||||
"✖ Вы можете ставить только разрешенные блоки!");
|
||||
msgReloaded = config.getString("admin_mode.messages.reloaded", "✓ Конфигурация AdminMode перезагружена!");
|
||||
|
||||
msgPrefix = config.getString("messages.prefix", "§8[§bAdminMode§8] ");
|
||||
msgEnterSoft = config.getString("messages.enter_soft", "§aВошёл в режим §bSOFT MODER");
|
||||
msgEnterHard = config.getString("messages.enter_hard", "§aВошёл в режим §cHARD MODER");
|
||||
msgExitMode = config.getString("messages.exit", "§7Режим деактивирован. Состояние восстановлено.");
|
||||
msgCoreProtectUnavailable = config.getString("messages.cp_unavailable",
|
||||
"§cCoreProtect не найден. Hard режим работает без CP.");
|
||||
msgSessionRecovered = config.getString("messages.session_recovered",
|
||||
"§eВаша активная сессия модерации была восстановлена и корректно завершена.");
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
@@ -143,6 +193,54 @@ public class ConfigManager {
|
||||
return dialogsEnabled;
|
||||
}
|
||||
|
||||
public String getBaseGroup() {
|
||||
return baseGroup;
|
||||
}
|
||||
|
||||
public String getSoftGroup() {
|
||||
return softGroup;
|
||||
}
|
||||
|
||||
public String getHardGroup() {
|
||||
return hardGroup;
|
||||
}
|
||||
|
||||
public String getActiveGroup(ModeratorProfile profile) {
|
||||
return profile != null && profile.isHardProfile() ? hardGroup : softGroup;
|
||||
}
|
||||
|
||||
public Material getSoftPanelMaterial() {
|
||||
return softPanelMaterial;
|
||||
}
|
||||
|
||||
public Material getHardPanelMaterial() {
|
||||
return hardPanelMaterial;
|
||||
}
|
||||
|
||||
public Material getCpInspectorMaterial() {
|
||||
return cpInspectorMaterial;
|
||||
}
|
||||
|
||||
public boolean isCoreProtectEnabled() {
|
||||
return coreProtectEnabled;
|
||||
}
|
||||
|
||||
public int getCoreProtectDefaultRadius() {
|
||||
return coreProtectDefaultRadius;
|
||||
}
|
||||
|
||||
public String getCoreProtectDefaultTime() {
|
||||
return coreProtectDefaultTime;
|
||||
}
|
||||
|
||||
public boolean isSessionLoggingEnabled() {
|
||||
return sessionLoggingEnabled;
|
||||
}
|
||||
|
||||
public String getSessionLogFile() {
|
||||
return sessionLogFile;
|
||||
}
|
||||
|
||||
public boolean isPreventItemDrop() {
|
||||
return preventItemDrop;
|
||||
}
|
||||
@@ -254,4 +352,49 @@ public class ConfigManager {
|
||||
public String getMsgReloaded() {
|
||||
return msgReloaded;
|
||||
}
|
||||
|
||||
public String getMsgPrefix() {
|
||||
return msgPrefix;
|
||||
}
|
||||
|
||||
public String getMsgEnterSoft() {
|
||||
return msgEnterSoft;
|
||||
}
|
||||
|
||||
public String getMsgEnterHard() {
|
||||
return msgEnterHard;
|
||||
}
|
||||
|
||||
public String getMsgExitMode() {
|
||||
return msgExitMode;
|
||||
}
|
||||
|
||||
public String getMsgCoreProtectUnavailable() {
|
||||
return msgCoreProtectUnavailable;
|
||||
}
|
||||
|
||||
public String getMsgSessionRecovered() {
|
||||
return msgSessionRecovered;
|
||||
}
|
||||
|
||||
public String getPrefixedMessage(String message) {
|
||||
if (message == null || message.isEmpty()) {
|
||||
return msgPrefix;
|
||||
}
|
||||
return msgPrefix + message;
|
||||
}
|
||||
|
||||
private Material readMaterial(String path, Material fallback) {
|
||||
String value = config.getString(path);
|
||||
if (value == null || value.isBlank()) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
try {
|
||||
return Material.valueOf(value.trim().toUpperCase());
|
||||
} catch (IllegalArgumentException exception) {
|
||||
plugin.getLogger().warning("Неизвестный material в конфиге " + path + ": " + value);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ import net.kyori.adventure.text.event.ClickCallback;
|
||||
import org.blz.adminmode.commands.AdminModeCommand;
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.blz.adminmode.moderation.ModeratorProfile;
|
||||
import org.blz.adminmode.moderation.PlayerListAction;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@@ -21,6 +24,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class AdminDialogService {
|
||||
|
||||
private static final ClickCallback.Options CALLBACK_OPTIONS = ClickCallback.Options.builder()
|
||||
@@ -38,19 +42,19 @@ public class AdminDialogService {
|
||||
this.adminModeCommand = adminModeCommand;
|
||||
}
|
||||
|
||||
public boolean openProfileSelectionDialog(Player player) {
|
||||
public boolean openModeSelectDialog(Player player) {
|
||||
if (!configManager.isDialogsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<ModeratorProfile> profiles = adminModeCommand.getAvailableProfiles(player);
|
||||
if (profiles.isEmpty()) {
|
||||
player.sendMessage(configManager.getMsgNoPermission());
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgNoPermission()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profiles.size() == 1) {
|
||||
adminModeCommand.toggleAdminMode(player, profiles.get(0));
|
||||
adminModeCommand.activateProfile(player, profiles.get(0));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -58,31 +62,37 @@ public class AdminDialogService {
|
||||
List<ActionButton> buttons = new ArrayList<>();
|
||||
for (ModeratorProfile profile : profiles) {
|
||||
buttons.add(actionButton(
|
||||
profile.getDisplayName(),
|
||||
profile.isHardProfile() ? "🔨 Hard Moder" : "🔍 Soft Moder",
|
||||
profile.isHardProfile()
|
||||
? "Полный режим модерации с сильными действиями"
|
||||
: "Базовый режим модерации для повседневной работы",
|
||||
? "Полный режим модерации, CoreProtect и сильные действия"
|
||||
: "Наблюдение, spectate, invsee и безопасные действия",
|
||||
(response, audience) -> withPlayer(audience, moderator -> {
|
||||
adminModeCommand.toggleAdminMode(moderator, profile);
|
||||
adminModeCommand.activateProfile(moderator, profile);
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
if (adminModeCommand.isInAdminMode(moderator.getUniqueId())) {
|
||||
openModerationPanel(moderator);
|
||||
openCurrentPanel(moderator);
|
||||
}
|
||||
});
|
||||
})));
|
||||
}
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("Выбор профиля модерации"))
|
||||
buttons.add(actionButton(
|
||||
"Закрыть",
|
||||
"Закрыть выбор режима",
|
||||
(response, audience) -> {
|
||||
}));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("⚙ Режим модерации"))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(Component.text(
|
||||
"Выберите профиль, с которым хотите войти в admin mode. Soft — для обычной модерации, Hard — для телепортов и сильных действий."),
|
||||
"Выберите режим входа. Soft — наблюдение и безопасная модерация. Hard — полный контроль, телепорты и CoreProtect."),
|
||||
360)))
|
||||
.inputs(List.of())
|
||||
.build();
|
||||
|
||||
showDialog(player, base, DialogType.multiAction(buttons, closeButton(), 2));
|
||||
showDialog(player, base, DialogType.multiAction(buttons, closeButton(), 1));
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось открыть dialog выбора профиля: " + exception.getMessage());
|
||||
@@ -90,7 +100,21 @@ public class AdminDialogService {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openModerationPanel(Player player) {
|
||||
public boolean openCurrentPanel(Player player) {
|
||||
UUID playerId = player.getUniqueId();
|
||||
ModeratorProfile profile = adminModeCommand.getActiveProfile(playerId);
|
||||
if (profile == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile.isHardProfile()) {
|
||||
return openHardPanel(player);
|
||||
}
|
||||
|
||||
return openSoftPanel(player);
|
||||
}
|
||||
|
||||
public boolean openSoftPanel(Player player) {
|
||||
if (!configManager.isDialogsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
@@ -100,70 +124,194 @@ public class AdminDialogService {
|
||||
}
|
||||
|
||||
try {
|
||||
UUID playerId = player.getUniqueId();
|
||||
ModeratorProfile profile = adminModeCommand.getActiveProfile(playerId);
|
||||
boolean hardProfile = profile != null && profile.isHardProfile();
|
||||
|
||||
List<ActionButton> buttons = new ArrayList<>();
|
||||
buttons.add(actionButton(
|
||||
"Скорости и режим",
|
||||
"Открыть слайдеры скорости и переключатель бессмертия",
|
||||
"👁 Spectate игрока",
|
||||
"Выбрать игрока для режима наблюдения",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(
|
||||
() -> openPlayerListDialog(moderator, PlayerListAction.SPECTATE)))));
|
||||
buttons.add(actionButton(
|
||||
"📍 TP к игроку",
|
||||
"Выбрать игрока для телепортации",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(
|
||||
() -> openPlayerListDialog(moderator, PlayerListAction.TELEPORT)))));
|
||||
buttons.add(actionButton(
|
||||
"🎒 Invsee игрока",
|
||||
"Открыть инвентарь игрока только для просмотра",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(
|
||||
() -> openPlayerListDialog(moderator, PlayerListAction.INVSEE)))));
|
||||
buttons.add(actionButton(
|
||||
"📋 Список онлайн",
|
||||
"Показать онлайн-игроков и их координаты",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openOnlineListDialog(moderator)))));
|
||||
buttons.add(actionButton(
|
||||
"🎛 Настройки режима",
|
||||
"Скорость полёта, ходьбы и бессмертие",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openSpeedPanel(moderator)))));
|
||||
buttons.add(actionButton(
|
||||
"Выключить admin mode",
|
||||
"🚪 Выйти из режима",
|
||||
"Восстановить сохраненное состояние и снять временные права",
|
||||
(response, audience) -> withPlayer(audience, adminModeCommand::toggleAdminMode)));
|
||||
|
||||
if (hardProfile) {
|
||||
buttons.add(actionButton(
|
||||
"Телепорт к игроку",
|
||||
"Телепортировать себя к цели из поля ниже",
|
||||
(response, audience) -> withPlayer(audience, moderator -> {
|
||||
String targetName = normalizeTarget(response.getText("target_player"));
|
||||
adminModeCommand.teleportToPlayer(moderator, targetName);
|
||||
})));
|
||||
buttons.add(actionButton(
|
||||
"Игрок ко мне",
|
||||
"Телепортировать цель к себе",
|
||||
(response, audience) -> withPlayer(audience, moderator -> {
|
||||
String targetName = normalizeTarget(response.getText("target_player"));
|
||||
adminModeCommand.teleportPlayerHere(moderator, targetName);
|
||||
})));
|
||||
buttons.add(actionButton(
|
||||
"Сбросить способности",
|
||||
"Отключить полет, бессмертие и скорости у цели",
|
||||
(response, audience) -> withPlayer(audience, moderator -> {
|
||||
String targetName = normalizeTarget(response.getText("target_player"));
|
||||
adminModeCommand.forceDisableTarget(moderator, targetName);
|
||||
})));
|
||||
}
|
||||
(response, audience) -> withPlayer(audience, moderator -> adminModeCommand.exitMode(moderator))));
|
||||
|
||||
List<Component> lines = new ArrayList<>();
|
||||
lines.add(Component.text("Активный профиль: " + (profile == null ? "unknown" : profile.getDisplayName())));
|
||||
lines.add(Component.text(hardProfile
|
||||
? "Hard профиль открывает телепорты и сильные действия."
|
||||
: "Soft профиль оставляет только безопасные действия и настройку режима."));
|
||||
lines.add(Component.text("Для действий по цели используйте поле с ником игрока."));
|
||||
lines.add(Component.text("Soft Moder предназначен для наблюдения, инвентарей и безопасной телепортации."));
|
||||
lines.add(Component.text("Используйте компас в руке как panel item или /adminmode для повторного открытия."));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("Панель модератора"))
|
||||
DialogBase base = DialogBase.builder(Component.text("🔍 Soft Moder"))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(joinLines(lines), 360)))
|
||||
.inputs(List.of(DialogInput.text(
|
||||
"target_player",
|
||||
320,
|
||||
Component.text("Ник цели"),
|
||||
true,
|
||||
"",
|
||||
16,
|
||||
null)))
|
||||
.inputs(List.of())
|
||||
.build();
|
||||
|
||||
showDialog(player, base, DialogType.multiAction(buttons, closeButton(), 2));
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось открыть moderation dialog: " + exception.getMessage());
|
||||
plugin.getLogger().warning("Не удалось открыть soft panel dialog: " + exception.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openHardPanel(Player player) {
|
||||
if (!configManager.isDialogsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
List<ActionButton> buttons = new ArrayList<>();
|
||||
buttons.add(actionButton(
|
||||
"👁 Spectate игрока",
|
||||
"Выбрать игрока для режима наблюдения",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(
|
||||
() -> openPlayerListDialog(moderator, PlayerListAction.SPECTATE)))));
|
||||
buttons.add(actionButton(
|
||||
"📍 TP к игроку",
|
||||
"Телепорт к выбранному игроку",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(
|
||||
() -> openPlayerListDialog(moderator, PlayerListAction.TELEPORT)))));
|
||||
buttons.add(actionButton(
|
||||
"🎒 Invsee игрока",
|
||||
"Открыть инвентарь игрока только для просмотра",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(
|
||||
() -> openPlayerListDialog(moderator, PlayerListAction.INVSEE)))));
|
||||
buttons.add(actionButton(
|
||||
"📋 Список онлайн",
|
||||
"Показать онлайн-игроков и координаты",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openOnlineListDialog(moderator)))));
|
||||
buttons.add(actionButton(
|
||||
"🔍 Lookup логи CP",
|
||||
"Открыть форму поиска по логам CoreProtect",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openLookupDialog(moderator)))));
|
||||
buttons.add(actionButton(
|
||||
"↩ Откат области CP",
|
||||
"Настроить rollback с подтверждением",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openRollbackDialog(moderator)))));
|
||||
buttons.add(actionButton(
|
||||
"🎛 Настройки режима",
|
||||
"Скорость полёта, ходьбы и бессмертие",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openSpeedPanel(moderator)))));
|
||||
buttons.add(actionButton(
|
||||
"🚪 Выйти из режима",
|
||||
"Восстановить сохраненное состояние и выйти из hard режима",
|
||||
(response, audience) -> withPlayer(audience, moderator -> adminModeCommand.exitMode(moderator))));
|
||||
|
||||
List<Component> lines = new ArrayList<>();
|
||||
lines.add(Component.text("Hard Moder включает полный доступ к сильным действиям и CoreProtect."));
|
||||
lines.add(Component.text("Используйте Nether Star для панели и Stick для переключения co inspect."));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("🔨 Hard Moder"))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(joinLines(lines), 360)))
|
||||
.inputs(List.of())
|
||||
.build();
|
||||
|
||||
showDialog(player, base, DialogType.multiAction(buttons, closeButton(), 2));
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось открыть hard panel dialog: " + exception.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openPlayerListDialog(Player player, PlayerListAction action) {
|
||||
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
List<ActionButton> buttons = new ArrayList<>();
|
||||
for (Player target : Bukkit.getOnlinePlayers()) {
|
||||
if (target.getUniqueId().equals(player.getUniqueId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buttons.add(actionButton(
|
||||
target.getName(),
|
||||
target.getWorld().getName() + " | " + target.getLocation().getBlockX() + ", "
|
||||
+ target.getLocation().getBlockY() + ", " + target.getLocation().getBlockZ(),
|
||||
(response, audience) -> withPlayer(audience, moderator ->
|
||||
adminModeCommand.performPlayerListAction(moderator, action, target.getName()))));
|
||||
}
|
||||
|
||||
if (buttons.isEmpty()) {
|
||||
buttons.add(actionButton(
|
||||
"Нет целей",
|
||||
"Кроме вас сейчас никого нет онлайн",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openCurrentPanel(moderator)))));
|
||||
}
|
||||
|
||||
ActionButton backButton = actionButton(
|
||||
"Назад",
|
||||
"Вернуться к основной панели",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openCurrentPanel(moderator))));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text(dialogTitleForAction(action)))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(Component.text(dialogBodyForAction(action)), 360)))
|
||||
.inputs(List.of())
|
||||
.build();
|
||||
|
||||
showDialog(player, base, DialogType.multiAction(buttons, backButton, 2));
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось открыть список игроков: " + exception.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openOnlineListDialog(Player player) {
|
||||
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
List<Component> lines = new ArrayList<>(adminModeCommand.getOnlineSummary(player));
|
||||
ActionButton backButton = actionButton(
|
||||
"Назад",
|
||||
"Вернуться к панели режима",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openCurrentPanel(moderator))));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("📋 Онлайн список"))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(joinLines(lines), 360)))
|
||||
.inputs(List.of())
|
||||
.build();
|
||||
|
||||
showDialog(player, base, DialogType.multiAction(List.of(), backButton, 1));
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось открыть онлайн список: " + exception.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -193,20 +341,20 @@ public class AdminDialogService {
|
||||
flySpeed == null ? currentFly : flySpeed,
|
||||
walkSpeed == null ? currentWalk : walkSpeed,
|
||||
godMode != null && godMode);
|
||||
schedule(() -> openModerationPanel(moderator));
|
||||
schedule(() -> openCurrentPanel(moderator));
|
||||
}));
|
||||
|
||||
ActionButton backButton = actionButton(
|
||||
"Назад",
|
||||
"Вернуться в основную панель модератора",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openModerationPanel(moderator))));
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openCurrentPanel(moderator))));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("Настройка admin mode"))
|
||||
DialogBase base = DialogBase.builder(Component.text("🎛 Настройка режима"))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(Component.text(
|
||||
"Настройте полет, скорость передвижения и режим бессмертия через новые элементы dialog API."),
|
||||
"Настройте скорость полёта, скорость ходьбы и бессмертие для текущей сессии модерации."),
|
||||
360)))
|
||||
.inputs(List.of(
|
||||
DialogInput.numberRange(
|
||||
@@ -243,7 +391,135 @@ public class AdminDialogService {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openLookupDialog(Player player) {
|
||||
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ActionButton lookupButton = actionButton(
|
||||
"🔍 Найти",
|
||||
"Выполнить co lookup с указанными параметрами",
|
||||
(response, audience) -> withPlayer(audience, moderator -> {
|
||||
String targetName = normalizeTarget(response.getText("cp_player"));
|
||||
Integer radius = parseInteger(response.getText("cp_radius"), configManager.getCoreProtectDefaultRadius());
|
||||
String time = normalizeTime(response.getText("cp_time"), configManager.getCoreProtectDefaultTime());
|
||||
adminModeCommand.executeCPLookup(moderator, targetName, radius, time);
|
||||
schedule(() -> openHardPanel(moderator));
|
||||
}));
|
||||
|
||||
ActionButton backButton = actionButton(
|
||||
"Назад",
|
||||
"Вернуться в Hard Moder panel",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openHardPanel(moderator))));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("🔍 Поиск в логах CP"))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(Component.text(
|
||||
"Фильтр по игроку опционален. Радиус и время можно оставить стандартными."), 360)))
|
||||
.inputs(List.of(
|
||||
DialogInput.text("cp_player", 300, Component.text("Игрок (пусто = все)"), false, "", 16, null),
|
||||
DialogInput.text("cp_radius", 180, Component.text("Радиус"), true,
|
||||
String.valueOf(configManager.getCoreProtectDefaultRadius()), 3, null),
|
||||
DialogInput.text("cp_time", 180, Component.text("Время"), true,
|
||||
configManager.getCoreProtectDefaultTime(), 4, null)))
|
||||
.build();
|
||||
|
||||
showDialog(player, base, DialogType.multiAction(List.of(lookupButton), backButton, 1));
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось открыть lookup dialog: " + exception.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openRollbackDialog(Player player) {
|
||||
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ActionButton confirmButton = actionButton(
|
||||
"✅ Далее к подтверждению",
|
||||
"Открыть подтверждение rollback",
|
||||
(response, audience) -> withPlayer(audience, moderator -> {
|
||||
int radius = parseInteger(response.getText("rollback_radius"), configManager.getCoreProtectDefaultRadius());
|
||||
String time = normalizeTime(response.getText("rollback_time"), configManager.getCoreProtectDefaultTime());
|
||||
String targetPlayer = normalizeTarget(response.getText("rollback_player"));
|
||||
schedule(() -> openRollbackConfirmDialog(moderator, radius, time, targetPlayer));
|
||||
}));
|
||||
|
||||
ActionButton backButton = actionButton(
|
||||
"Назад",
|
||||
"Вернуться в Hard Moder panel",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openHardPanel(moderator))));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("↩ Rollback CoreProtect"))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(Component.text(
|
||||
"Укажите радиус, временной диапазон и при необходимости игрока. Затем подтвердите откат."), 360)))
|
||||
.inputs(List.of(
|
||||
DialogInput.text("rollback_radius", 180, Component.text("Радиус"), true,
|
||||
String.valueOf(configManager.getCoreProtectDefaultRadius()), 3, null),
|
||||
DialogInput.text("rollback_time", 180, Component.text("Время"), true,
|
||||
configManager.getCoreProtectDefaultTime(), 4, null),
|
||||
DialogInput.text("rollback_player", 300, Component.text("Игрок (опционально)"), false,
|
||||
"", 16, null)))
|
||||
.build();
|
||||
|
||||
showDialog(player, base, DialogType.multiAction(List.of(confirmButton), backButton, 1));
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось открыть rollback dialog: " + exception.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openRollbackConfirmDialog(Player player, int radius, String time, String targetPlayer) {
|
||||
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
List<Component> lines = new ArrayList<>();
|
||||
lines.add(Component.text("Радиус: " + radius + " блоков"));
|
||||
lines.add(Component.text("Время: " + time));
|
||||
lines.add(Component.text(targetPlayer.isEmpty() ? "Игрок: все" : "Игрок: " + targetPlayer));
|
||||
|
||||
ActionButton confirmButton = actionButton(
|
||||
"✅ Откатить",
|
||||
"Подтвердить rollback по указанным параметрам",
|
||||
(response, audience) -> withPlayer(audience, moderator -> {
|
||||
adminModeCommand.executeCPRollback(moderator, radius, time, targetPlayer);
|
||||
schedule(() -> openHardPanel(moderator));
|
||||
}));
|
||||
ActionButton cancelButton = actionButton(
|
||||
"❌ Отмена",
|
||||
"Отменить rollback и вернуться назад",
|
||||
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openRollbackDialog(moderator))));
|
||||
|
||||
DialogBase base = DialogBase.builder(Component.text("⚠ Подтвердить откат?"))
|
||||
.canCloseWithEscape(true)
|
||||
.pause(false)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.body(List.of(DialogBody.plainMessage(joinLines(lines), 360)))
|
||||
.inputs(List.of())
|
||||
.build();
|
||||
|
||||
showDialog(player, base, DialogType.multiAction(List.of(confirmButton, cancelButton), closeButton(), 1));
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось открыть подтверждение rollback: " + exception.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void showDialog(Player player, DialogBase base, io.papermc.paper.registry.data.dialog.type.DialogType type) {
|
||||
player.closeInventory();
|
||||
Dialog dialog = Dialog.create(factory -> {
|
||||
var builder = factory.empty();
|
||||
builder.base(base);
|
||||
@@ -291,6 +567,46 @@ public class AdminDialogService {
|
||||
return targetName == null ? "" : targetName.trim();
|
||||
}
|
||||
|
||||
private String normalizeTime(String input, String fallback) {
|
||||
if (input == null || input.isBlank()) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
String normalized = input.trim().toLowerCase();
|
||||
if (normalized.matches("\\d+[smhdw]")) {
|
||||
return normalized;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private Integer parseInteger(String value, int fallback) {
|
||||
if (value == null || value.isBlank()) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException exception) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
private String dialogTitleForAction(PlayerListAction action) {
|
||||
return switch (action) {
|
||||
case SPECTATE -> "👁 Spectate игрока";
|
||||
case TELEPORT -> "📍 TP к игроку";
|
||||
case INVSEE -> "🎒 Invsee игрока";
|
||||
};
|
||||
}
|
||||
|
||||
private String dialogBodyForAction(PlayerListAction action) {
|
||||
return switch (action) {
|
||||
case SPECTATE -> "Выберите игрока, за которым хотите наблюдать.";
|
||||
case TELEPORT -> "Выберите игрока, к которому хотите телепортироваться.";
|
||||
case INVSEE -> "Выберите игрока, чей инвентарь хотите открыть.";
|
||||
};
|
||||
}
|
||||
|
||||
private float clamp(float value, float min, float max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.blz.adminmode.integrations;
|
||||
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class CoreProtectIntegration {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final ConfigManager configManager;
|
||||
|
||||
public CoreProtectIntegration(Plugin plugin, ConfigManager configManager) {
|
||||
this.plugin = plugin;
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return configManager.isCoreProtectEnabled() && plugin.getServer().getPluginManager().getPlugin("CoreProtect") != null;
|
||||
}
|
||||
|
||||
public boolean toggleInspector(Player player) {
|
||||
if (!isAvailable()) {
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgCoreProtectUnavailable()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return Bukkit.dispatchCommand(player, "co inspect");
|
||||
}
|
||||
|
||||
public boolean lookup(Player player, String targetPlayer, int radius, String time) {
|
||||
if (!isAvailable()) {
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgCoreProtectUnavailable()));
|
||||
return false;
|
||||
}
|
||||
|
||||
StringBuilder command = new StringBuilder("co lookup");
|
||||
appendPlayerFilter(command, targetPlayer);
|
||||
command.append(" r:").append(normalizeRadius(radius));
|
||||
command.append(" t:").append(normalizeTime(time));
|
||||
return Bukkit.dispatchCommand(player, command.toString());
|
||||
}
|
||||
|
||||
public boolean rollback(Player player, int radius, String time, String targetPlayer) {
|
||||
if (!isAvailable()) {
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgCoreProtectUnavailable()));
|
||||
return false;
|
||||
}
|
||||
|
||||
StringBuilder command = new StringBuilder("co rollback");
|
||||
appendPlayerFilter(command, targetPlayer);
|
||||
command.append(" r:").append(normalizeRadius(radius));
|
||||
command.append(" t:").append(normalizeTime(time));
|
||||
return Bukkit.dispatchCommand(player, command.toString());
|
||||
}
|
||||
|
||||
private void appendPlayerFilter(StringBuilder command, String targetPlayer) {
|
||||
String sanitized = sanitizePlayerName(targetPlayer);
|
||||
if (!sanitized.isEmpty()) {
|
||||
command.append(" u:").append(sanitized);
|
||||
}
|
||||
}
|
||||
|
||||
private int normalizeRadius(int radius) {
|
||||
return Math.max(1, Math.min(100, radius));
|
||||
}
|
||||
|
||||
private String normalizeTime(String time) {
|
||||
String fallback = configManager.getCoreProtectDefaultTime();
|
||||
if (time == null || time.isBlank()) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
String normalized = time.trim().toLowerCase();
|
||||
if (!normalized.matches("\\d+[smhdw]")) {
|
||||
return fallback;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private String sanitizePlayerName(String targetPlayer) {
|
||||
if (targetPlayer == null || targetPlayer.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String normalized = targetPlayer.trim();
|
||||
if (normalized.matches("[A-Za-z0-9_]{1,16}")) {
|
||||
return normalized;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.blz.adminmode.integrations;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.model.user.User;
|
||||
import net.luckperms.api.node.Node;
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.blz.adminmode.moderation.ModeSession;
|
||||
import org.blz.adminmode.moderation.ModeratorProfile;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class LuckPermsIntegration {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final ConfigManager configManager;
|
||||
|
||||
public LuckPermsIntegration(Plugin plugin, ConfigManager configManager) {
|
||||
this.plugin = plugin;
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
public String resolveCurrentGroup(Player player) {
|
||||
if (!configManager.isUseLuckPerms()) {
|
||||
return configManager.getBaseGroup();
|
||||
}
|
||||
|
||||
try {
|
||||
User user = resolveUser(player);
|
||||
if (user == null) {
|
||||
return configManager.getBaseGroup();
|
||||
}
|
||||
return user.getPrimaryGroup();
|
||||
} catch (IllegalStateException exception) {
|
||||
return configManager.getBaseGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public void enterMode(Player player, ModeratorProfile profile) {
|
||||
if (!configManager.isUseLuckPerms()) {
|
||||
player.setOp(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
User user = resolveUser(player);
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeKnownGroups(user);
|
||||
user.data().add(groupNode(configManager.getActiveGroup(profile)));
|
||||
saveUser(user);
|
||||
} catch (IllegalStateException exception) {
|
||||
player.setOp(true);
|
||||
plugin.getLogger().warning("LuckPerms не найден, используется OP для " + player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void exitMode(Player player, ModeSession session) {
|
||||
if (!configManager.isUseLuckPerms()) {
|
||||
player.setOp(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
User user = resolveUser(player);
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeKnownGroups(user);
|
||||
String restoreGroup = session.getPreviousGroup();
|
||||
if (restoreGroup == null || restoreGroup.isBlank() || "op".equalsIgnoreCase(restoreGroup)) {
|
||||
restoreGroup = configManager.getBaseGroup();
|
||||
}
|
||||
user.data().add(groupNode(restoreGroup));
|
||||
saveUser(user);
|
||||
} catch (IllegalStateException exception) {
|
||||
player.setOp(false);
|
||||
plugin.getLogger().warning("LuckPerms не найден, снят OP у " + player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private User resolveUser(Player player) {
|
||||
LuckPerms luckPerms = LuckPermsProvider.get();
|
||||
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
|
||||
if (user == null) {
|
||||
user = luckPerms.getUserManager().loadUser(player.getUniqueId()).join();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private void removeKnownGroups(User user) {
|
||||
user.data().remove(groupNode(configManager.getBaseGroup()));
|
||||
user.data().remove(groupNode(configManager.getSoftGroup()));
|
||||
user.data().remove(groupNode(configManager.getHardGroup()));
|
||||
}
|
||||
|
||||
private Node groupNode(String group) {
|
||||
return Node.builder("group." + group).build();
|
||||
}
|
||||
|
||||
private void saveUser(User user) {
|
||||
LuckPermsProvider.get().getUserManager().saveUser(user);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,39 @@
|
||||
package org.blz.adminmode.listeners;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.blz.adminmode.commands.AdminModeCommand;
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.blz.adminmode.dialog.AdminDialogService;
|
||||
import org.blz.adminmode.moderation.ModeManager;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntityPickupItemEvent;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AdminModeListener implements Listener {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final AdminModeCommand adminModeCommand;
|
||||
private final ConfigManager configManager;
|
||||
private final ModeManager modeManager;
|
||||
private final AdminDialogService dialogService;
|
||||
|
||||
public AdminModeListener(Plugin plugin, AdminModeCommand adminModeCommand, ConfigManager configManager) {
|
||||
this.plugin = plugin;
|
||||
public AdminModeListener(
|
||||
AdminModeCommand adminModeCommand,
|
||||
ConfigManager configManager,
|
||||
ModeManager modeManager,
|
||||
AdminDialogService dialogService) {
|
||||
this.adminModeCommand = adminModeCommand;
|
||||
this.configManager = configManager;
|
||||
this.modeManager = modeManager;
|
||||
this.dialogService = dialogService;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
@@ -37,7 +44,7 @@ public class AdminModeListener implements Listener {
|
||||
if (!configManager.isPreventItemDrop()) return;
|
||||
|
||||
e.setCancelled(true);
|
||||
player.sendMessage(ChatColor.RED + configManager.getMsgItemDropDenied());
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgItemDropDenied()));
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
@@ -57,6 +64,11 @@ public class AdminModeListener implements Listener {
|
||||
if (!(e.getWhoClicked() instanceof Player)) return;
|
||||
|
||||
Player player = (Player) e.getWhoClicked();
|
||||
|
||||
if (modeManager.isReadOnlyViewedInventory(player, e.getView().getTopInventory())) {
|
||||
e.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) return;
|
||||
if (!configManager.isPreventContainerTransfer()) return;
|
||||
@@ -65,7 +77,7 @@ public class AdminModeListener implements Listener {
|
||||
if (e.getClickedInventory().getHolder() != null &&
|
||||
!e.getClickedInventory().getHolder().equals(player)) {
|
||||
e.setCancelled(true);
|
||||
player.sendMessage(ChatColor.RED + configManager.getMsgContainerTransferDenied());
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgContainerTransferDenied()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -74,7 +86,7 @@ public class AdminModeListener implements Listener {
|
||||
if (e.getView().getTopInventory().getHolder() != null &&
|
||||
!e.getView().getTopInventory().getHolder().equals(player)) {
|
||||
e.setCancelled(true);
|
||||
player.sendMessage(ChatColor.RED + configManager.getMsgContainerTransferDenied());
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgContainerTransferDenied()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,9 +96,25 @@ public class AdminModeListener implements Listener {
|
||||
Player player = e.getPlayer();
|
||||
|
||||
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) return;
|
||||
|
||||
if ((e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK)
|
||||
&& e.getItem() != null
|
||||
&& modeManager.isPanelItem(e.getItem())) {
|
||||
e.setCancelled(true);
|
||||
|
||||
String panelType = modeManager.getPanelType(e.getItem());
|
||||
if (ModeManager.PANEL_CP_INSPECTOR.equals(panelType)) {
|
||||
adminModeCommand.toggleInspector(player);
|
||||
return;
|
||||
}
|
||||
|
||||
dialogService.openCurrentPanel(player);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configManager.isPreventItemUse()) return;
|
||||
|
||||
if (e.getAction() == org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) {
|
||||
if (e.getAction() == Action.RIGHT_CLICK_BLOCK) {
|
||||
if (e.getClickedBlock() != null) {
|
||||
Material blockType = e.getClickedBlock().getType();
|
||||
|
||||
@@ -102,7 +130,7 @@ public class AdminModeListener implements Listener {
|
||||
|
||||
if (e.getItem() != null && e.getItem().getType() != Material.AIR) {
|
||||
e.setCancelled(true);
|
||||
player.sendMessage(ChatColor.RED + configManager.getMsgItemUseDenied());
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgItemUseDenied()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +152,6 @@ public class AdminModeListener implements Listener {
|
||||
}
|
||||
|
||||
e.setCancelled(true);
|
||||
player.sendMessage(ChatColor.RED + configManager.getMsgBlockPlaceDenied());
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgBlockPlaceDenied()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package org.blz.adminmode.listeners;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.blz.adminmode.commands.AdminModeCommand;
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.blz.adminmode.moderation.ModeratorProfile;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -11,8 +13,6 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerGameModeChangeEvent;
|
||||
import org.bukkit.event.player.PlayerToggleFlightEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GameModeChangeListener implements Listener {
|
||||
|
||||
private final AdminModeCommand adminModeCommand;
|
||||
@@ -31,11 +31,16 @@ public class GameModeChangeListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// КРИТИЧНО: Блокируем CREATIVE для всех в админ моде!
|
||||
if (event.getNewGameMode() == GameMode.CREATIVE) {
|
||||
ModeratorProfile activeProfile = adminModeCommand.getActiveProfile(player.getUniqueId());
|
||||
boolean hardProfile = activeProfile != null && activeProfile.isHardProfile();
|
||||
|
||||
if (event.getNewGameMode() == GameMode.CREATIVE && !hardProfile) {
|
||||
event.setCancelled(true);
|
||||
player.sendMessage(ChatColor.RED + "✖ В режиме модератора КРЕАТИВ запрещен!");
|
||||
player.sendMessage(ChatColor.GRAY + "Доступные режимы: SURVIVAL, SPECTATOR");
|
||||
player.sendMessage(configManager.getPrefixedMessage("§c✖ В режиме Soft Moder креатив запрещен!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getNewGameMode() == GameMode.CREATIVE && hardProfile) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,8 +51,8 @@ public class GameModeChangeListener implements Listener {
|
||||
|
||||
if (!allowedModes.contains(newMode)) {
|
||||
event.setCancelled(true);
|
||||
player.sendMessage(ChatColor.RED + "✖ В админ моде можно переключаться только на разрешенные режимы!");
|
||||
player.sendMessage(ChatColor.GRAY + "Разрешенные режимы: " + String.join(", ", allowedModes));
|
||||
player.sendMessage(configManager.getPrefixedMessage("§c✖ В админ моде можно переключаться только на разрешенные режимы!"));
|
||||
player.sendMessage(configManager.getPrefixedMessage("§7Разрешенные режимы: " + String.join(", ", allowedModes)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.blz.adminmode.listeners;
|
||||
|
||||
import org.blz.adminmode.moderation.ModeManager;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
|
||||
public class PlayerDeathListener implements Listener {
|
||||
|
||||
private final ModeManager modeManager;
|
||||
|
||||
public PlayerDeathListener(ModeManager modeManager) {
|
||||
this.modeManager = modeManager;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||
modeManager.handleHardModeDeath(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.blz.adminmode.listeners;
|
||||
|
||||
import org.blz.adminmode.commands.AdminModeCommand;
|
||||
import org.blz.adminmode.moderation.ModeManager;
|
||||
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 PlayerSessionListener implements Listener {
|
||||
|
||||
private final AdminModeCommand adminModeCommand;
|
||||
private final ModeManager modeManager;
|
||||
|
||||
public PlayerSessionListener(AdminModeCommand adminModeCommand, ModeManager modeManager) {
|
||||
this.adminModeCommand = adminModeCommand;
|
||||
this.modeManager = modeManager;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
modeManager.markQuit(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (!modeManager.hasSession(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.getServer().getScheduler().runTaskLater(
|
||||
org.blz.adminmode.AdminMode.getInstance(),
|
||||
() -> adminModeCommand.recoverSession(player),
|
||||
1L);
|
||||
}
|
||||
}
|
||||
410
src/main/java/org/blz/adminmode/moderation/ModeManager.java
Normal file
410
src/main/java/org/blz/adminmode/moderation/ModeManager.java
Normal file
@@ -0,0 +1,410 @@
|
||||
package org.blz.adminmode.moderation;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.blz.adminmode.integrations.LuckPermsIntegration;
|
||||
import org.blz.adminmode.session.SessionStorage;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ModeManager {
|
||||
|
||||
public static final String PANEL_SOFT = "soft";
|
||||
public static final String PANEL_HARD = "hard";
|
||||
public static final String PANEL_CP_INSPECTOR = "cp_inspector";
|
||||
|
||||
private final Plugin plugin;
|
||||
private final ConfigManager configManager;
|
||||
private final LuckPermsIntegration luckPermsIntegration;
|
||||
private final SessionStorage sessionStorage;
|
||||
private final ModeSessionLogger sessionLogger;
|
||||
private final NamespacedKey panelTypeKey;
|
||||
private final Map<UUID, ModeSession> activeSessions = new HashMap<>();
|
||||
|
||||
public ModeManager(
|
||||
Plugin plugin,
|
||||
ConfigManager configManager,
|
||||
LuckPermsIntegration luckPermsIntegration,
|
||||
SessionStorage sessionStorage,
|
||||
ModeSessionLogger sessionLogger) {
|
||||
this.plugin = plugin;
|
||||
this.configManager = configManager;
|
||||
this.luckPermsIntegration = luckPermsIntegration;
|
||||
this.sessionStorage = sessionStorage;
|
||||
this.sessionLogger = sessionLogger;
|
||||
this.panelTypeKey = new NamespacedKey(plugin, "panel_type");
|
||||
this.activeSessions.putAll(sessionStorage.loadSessions());
|
||||
}
|
||||
|
||||
public boolean hasSession(UUID playerId) {
|
||||
return activeSessions.containsKey(playerId);
|
||||
}
|
||||
|
||||
public ModeSession getSession(UUID playerId) {
|
||||
return activeSessions.get(playerId);
|
||||
}
|
||||
|
||||
public ModeSession getSession(Player player) {
|
||||
return player == null ? null : getSession(player.getUniqueId());
|
||||
}
|
||||
|
||||
public ModeratorProfile getProfile(UUID playerId) {
|
||||
ModeSession session = getSession(playerId);
|
||||
return session == null ? null : session.getProfile();
|
||||
}
|
||||
|
||||
public List<ModeratorProfile> getAvailableProfiles(Player player) {
|
||||
return ModeratorProfile.availableFor(player);
|
||||
}
|
||||
|
||||
public Collection<ModeSession> getActiveSessions() {
|
||||
return new ArrayList<>(activeSessions.values());
|
||||
}
|
||||
|
||||
public boolean enterMode(Player player, ModeratorProfile profile) {
|
||||
if (player == null || profile == null || hasSession(player.getUniqueId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String previousGroup = luckPermsIntegration.resolveCurrentGroup(player);
|
||||
String activeGroup = configManager.getActiveGroup(profile);
|
||||
ModeSession session = ModeSession.capture(player, profile, previousGroup, activeGroup);
|
||||
activeSessions.put(player.getUniqueId(), session);
|
||||
persistSessions();
|
||||
|
||||
preparePlayerForModeration(player, profile);
|
||||
luckPermsIntegration.enterMode(player, profile);
|
||||
sessionLogger.logEnter(session);
|
||||
|
||||
String enterMessage = profile.isHardProfile() ? configManager.getMsgEnterHard() : configManager.getMsgEnterSoft();
|
||||
player.sendMessage(configManager.getPrefixedMessage(enterMessage));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean exitMode(Player player) {
|
||||
return finishSession(player, false);
|
||||
}
|
||||
|
||||
public boolean recoverSession(Player player) {
|
||||
return finishSession(player, true);
|
||||
}
|
||||
|
||||
public void markQuit(Player player) {
|
||||
if (player != null && hasSession(player.getUniqueId())) {
|
||||
persistSessions();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canUseHardActions(CommandSender sender) {
|
||||
if (sender.hasPermission("adminmode.admin")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ModeSession session = getSession(player);
|
||||
return session != null && session.getProfile().isHardProfile();
|
||||
}
|
||||
|
||||
public boolean teleportToPlayer(Player moderator, Player target) {
|
||||
if (moderator == null || target == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
moderator.teleport(target.getLocation());
|
||||
moderator.sendMessage(configManager.getPrefixedMessage("§aТелепортация к §f" + target.getName() + "§a выполнена."));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean teleportPlayerHere(Player moderator, Player target) {
|
||||
if (moderator == null || target == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target.teleport(moderator.getLocation());
|
||||
moderator.sendMessage(configManager.getPrefixedMessage("§aИгрок §f" + target.getName() + "§a телепортирован к вам."));
|
||||
target.sendMessage(configManager.getPrefixedMessage("§eВас телепортировал модератор §f" + moderator.getName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean spectatePlayer(Player moderator, Player target) {
|
||||
if (moderator == null || target == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (moderator.equals(target)) {
|
||||
moderator.sendMessage(configManager.getPrefixedMessage("§cНельзя спектейтить самого себя."));
|
||||
return false;
|
||||
}
|
||||
|
||||
moderator.setGameMode(GameMode.SPECTATOR);
|
||||
moderator.setSpectatorTarget((Entity) target);
|
||||
moderator.sendMessage(configManager.getPrefixedMessage("§bВы наблюдаете за §f" + target.getName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean openInvsee(Player moderator, Player target) {
|
||||
if (moderator == null || target == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
moderator.openInventory(target.getInventory());
|
||||
moderator.sendMessage(configManager.getPrefixedMessage("§7Открыт инвентарь игрока §f" + target.getName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void applyAdminSettings(Player player, float flySpeed, float walkSpeed, boolean invulnerable) {
|
||||
if (!hasSession(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.setAllowFlight(true);
|
||||
player.setFlying(true);
|
||||
player.setFlySpeed(clampSpeed(flySpeed, 0.1f));
|
||||
player.setWalkSpeed(clampSpeed(walkSpeed, 0.1f));
|
||||
player.setInvulnerable(invulnerable);
|
||||
}
|
||||
|
||||
public List<Player> listOtherOnlinePlayers(Player viewer) {
|
||||
List<Player> players = new ArrayList<>(plugin.getServer().getOnlinePlayers());
|
||||
players.removeIf(player -> viewer != null && player.getUniqueId().equals(viewer.getUniqueId()));
|
||||
players.sort(Comparator.comparing(Player::getName, String.CASE_INSENSITIVE_ORDER));
|
||||
return players;
|
||||
}
|
||||
|
||||
public List<Component> buildOnlineSummary(Player viewer) {
|
||||
List<Component> lines = new ArrayList<>();
|
||||
for (Player target : listOtherOnlinePlayers(viewer)) {
|
||||
Location location = target.getLocation();
|
||||
lines.add(Component.text(
|
||||
target.getName() + " | " + target.getWorld().getName() + " | " +
|
||||
location.getBlockX() + ", " + location.getBlockY() + ", " + location.getBlockZ(),
|
||||
NamedTextColor.GRAY));
|
||||
}
|
||||
if (lines.isEmpty()) {
|
||||
lines.add(Component.text("Нет других игроков онлайн.", NamedTextColor.DARK_GRAY));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
public void handleHardModeDeath(PlayerDeathEvent event) {
|
||||
Player player = event.getEntity();
|
||||
ModeSession session = getSession(player);
|
||||
if (session == null || !session.getProfile().isHardProfile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setKeepInventory(true);
|
||||
event.setKeepLevel(true);
|
||||
event.getDrops().clear();
|
||||
Location restoreLocation = session.getLocation();
|
||||
if (restoreLocation != null) {
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> player.teleport(restoreLocation));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPanelItem(ItemStack itemStack) {
|
||||
return getPanelType(itemStack) != null;
|
||||
}
|
||||
|
||||
public String getPanelType(ItemStack itemStack) {
|
||||
if (itemStack == null || itemStack.getType() == Material.AIR || !itemStack.hasItemMeta()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
PersistentDataContainer container = itemMeta.getPersistentDataContainer();
|
||||
if (!container.has(panelTypeKey, PersistentDataType.STRING)) {
|
||||
return null;
|
||||
}
|
||||
return container.get(panelTypeKey, PersistentDataType.STRING);
|
||||
}
|
||||
|
||||
public boolean isReadOnlyViewedInventory(Player viewer, org.bukkit.inventory.Inventory inventory) {
|
||||
if (viewer == null || inventory == null || inventory.getHolder() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(inventory.getHolder() instanceof Player target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasSession(viewer.getUniqueId()) && !viewer.getUniqueId().equals(target.getUniqueId());
|
||||
}
|
||||
|
||||
private boolean finishSession(Player player, boolean recovered) {
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ModeSession session = activeSessions.remove(player.getUniqueId());
|
||||
if (session == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
restorePlayerState(player, session);
|
||||
luckPermsIntegration.exitMode(player, session);
|
||||
persistSessions();
|
||||
|
||||
if (recovered) {
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgSessionRecovered()));
|
||||
sessionLogger.logRecovered(session);
|
||||
} else {
|
||||
player.sendMessage(configManager.getPrefixedMessage(configManager.getMsgExitMode()));
|
||||
sessionLogger.logExit(session);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void preparePlayerForModeration(Player player, ModeratorProfile profile) {
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
inventory.clear();
|
||||
inventory.setArmorContents(new ItemStack[4]);
|
||||
inventory.setItemInOffHand(new ItemStack(Material.AIR));
|
||||
|
||||
player.closeInventory();
|
||||
player.setSpectatorTarget(null);
|
||||
for (PotionEffect effect : player.getActivePotionEffects()) {
|
||||
player.removePotionEffect(effect.getType());
|
||||
}
|
||||
|
||||
player.setFireTicks(0);
|
||||
player.setHealth(resolveMaxHealth(player));
|
||||
player.setFoodLevel(20);
|
||||
player.setSaturation(20.0f);
|
||||
player.setExhaustion(0.0f);
|
||||
player.setExp(0.0f);
|
||||
player.setLevel(0);
|
||||
player.setTotalExperience(0);
|
||||
player.setAllowFlight(true);
|
||||
player.setFlying(true);
|
||||
player.setFlySpeed(clampSpeed(configManager.getCustomFlySpeed(), 0.1f));
|
||||
player.setWalkSpeed(clampSpeed(configManager.getCustomWalkSpeed(), 0.1f));
|
||||
player.setInvulnerable(true);
|
||||
player.setGameMode(profile.isHardProfile() ? GameMode.CREATIVE : GameMode.SPECTATOR);
|
||||
|
||||
inventory.setItem(0, createPanelItem(profile.isHardProfile() ? configManager.getHardPanelMaterial() : configManager.getSoftPanelMaterial(),
|
||||
profile.isHardProfile() ? "Hard Panel" : "Soft Panel",
|
||||
profile.isHardProfile() ? NamedTextColor.RED : NamedTextColor.AQUA,
|
||||
profile.isHardProfile() ? PANEL_HARD : PANEL_SOFT));
|
||||
|
||||
if (profile.isHardProfile()) {
|
||||
inventory.setItem(1, createPanelItem(
|
||||
configManager.getCpInspectorMaterial(),
|
||||
"CP Inspector",
|
||||
NamedTextColor.YELLOW,
|
||||
PANEL_CP_INSPECTOR));
|
||||
}
|
||||
|
||||
if (configManager.isGivePresetBlocks()) {
|
||||
givePresetBlocks(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void restorePlayerState(Player player, ModeSession session) {
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
inventory.clear();
|
||||
inventory.setContents(session.getInventoryContents());
|
||||
inventory.setArmorContents(session.getArmorContents());
|
||||
inventory.setItemInOffHand(session.getOffHandItem());
|
||||
|
||||
player.setSpectatorTarget(null);
|
||||
player.setInvulnerable(false);
|
||||
player.setAllowFlight(false);
|
||||
player.setFlying(false);
|
||||
player.setFlySpeed(0.1f);
|
||||
player.setWalkSpeed(0.2f);
|
||||
|
||||
if (player.getAttribute(Attribute.MAX_HEALTH) != null) {
|
||||
player.getAttribute(Attribute.MAX_HEALTH).setBaseValue(session.getMaxHealth());
|
||||
}
|
||||
|
||||
player.setGameMode(session.getGameMode());
|
||||
if (session.getLocation() != null) {
|
||||
player.teleport(session.getLocation());
|
||||
}
|
||||
|
||||
player.setHealth(Math.min(resolveMaxHealth(player), session.getHealth()));
|
||||
player.setFoodLevel(session.getFoodLevel());
|
||||
player.setSaturation(session.getSaturation());
|
||||
player.setExhaustion(session.getExhaustion());
|
||||
player.setExp(session.getExp());
|
||||
player.setLevel(session.getExpLevel());
|
||||
player.setTotalExperience(session.getTotalExperience());
|
||||
|
||||
for (PotionEffect effect : player.getActivePotionEffects()) {
|
||||
player.removePotionEffect(effect.getType());
|
||||
}
|
||||
for (PotionEffect effect : session.getPotionEffects()) {
|
||||
player.addPotionEffect(effect);
|
||||
}
|
||||
|
||||
player.setFireTicks(session.getFireTicks());
|
||||
player.setAllowFlight(session.isAllowFlight());
|
||||
player.setFlying(session.isFlying());
|
||||
player.setFlySpeed(clampSpeed(session.getFlySpeed(), 0.1f));
|
||||
player.setWalkSpeed(clampSpeed(session.getWalkSpeed(), 0.1f));
|
||||
}
|
||||
|
||||
private void givePresetBlocks(Player player) {
|
||||
for (String blockString : configManager.getPresetBlocks()) {
|
||||
try {
|
||||
String[] parts = blockString.split(":");
|
||||
Material material = Material.valueOf(parts[0].trim().toUpperCase());
|
||||
int amount = parts.length > 1 ? Integer.parseInt(parts[1].trim()) : 64;
|
||||
player.getInventory().addItem(new ItemStack(material, Math.max(1, amount)));
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Неверный блок в preset_blocks: " + blockString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ItemStack createPanelItem(Material material, String title, NamedTextColor color, String panelType) {
|
||||
ItemStack itemStack = new ItemStack(material);
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
itemMeta.displayName(Component.text("[" + title + "]", color));
|
||||
itemMeta.getPersistentDataContainer().set(panelTypeKey, PersistentDataType.STRING, panelType);
|
||||
itemStack.setItemMeta(itemMeta);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private void persistSessions() {
|
||||
sessionStorage.saveSessions(activeSessions);
|
||||
}
|
||||
|
||||
private double resolveMaxHealth(Player player) {
|
||||
if (player.getAttribute(Attribute.MAX_HEALTH) != null) {
|
||||
return player.getAttribute(Attribute.MAX_HEALTH).getValue();
|
||||
}
|
||||
return 20.0D;
|
||||
}
|
||||
|
||||
private float clampSpeed(float value, float min) {
|
||||
return Math.max(min, Math.min(1.0f, value));
|
||||
}
|
||||
}
|
||||
253
src/main/java/org/blz/adminmode/moderation/ModeSession.java
Normal file
253
src/main/java/org/blz/adminmode/moderation/ModeSession.java
Normal file
@@ -0,0 +1,253 @@
|
||||
package org.blz.adminmode.moderation;
|
||||
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ModeSession {
|
||||
|
||||
private final UUID playerId;
|
||||
private final String playerName;
|
||||
private final ModeratorProfile profile;
|
||||
private final long startTimestamp;
|
||||
private final String previousGroup;
|
||||
private final String activeGroup;
|
||||
private final ItemStack[] inventoryContents;
|
||||
private final ItemStack[] armorContents;
|
||||
private final ItemStack offHandItem;
|
||||
private final Location location;
|
||||
private final GameMode gameMode;
|
||||
private final double health;
|
||||
private final double maxHealth;
|
||||
private final int foodLevel;
|
||||
private final float saturation;
|
||||
private final float exhaustion;
|
||||
private final int expLevel;
|
||||
private final float exp;
|
||||
private final int totalExperience;
|
||||
private final Collection<PotionEffect> potionEffects;
|
||||
private final boolean flying;
|
||||
private final boolean allowFlight;
|
||||
private final float flySpeed;
|
||||
private final float walkSpeed;
|
||||
private final int fireTicks;
|
||||
|
||||
public ModeSession(
|
||||
UUID playerId,
|
||||
String playerName,
|
||||
ModeratorProfile profile,
|
||||
long startTimestamp,
|
||||
String previousGroup,
|
||||
String activeGroup,
|
||||
ItemStack[] inventoryContents,
|
||||
ItemStack[] armorContents,
|
||||
ItemStack offHandItem,
|
||||
Location location,
|
||||
GameMode gameMode,
|
||||
double health,
|
||||
double maxHealth,
|
||||
int foodLevel,
|
||||
float saturation,
|
||||
float exhaustion,
|
||||
int expLevel,
|
||||
float exp,
|
||||
int totalExperience,
|
||||
Collection<PotionEffect> potionEffects,
|
||||
boolean flying,
|
||||
boolean allowFlight,
|
||||
float flySpeed,
|
||||
float walkSpeed,
|
||||
int fireTicks) {
|
||||
this.playerId = playerId;
|
||||
this.playerName = playerName;
|
||||
this.profile = profile;
|
||||
this.startTimestamp = startTimestamp;
|
||||
this.previousGroup = previousGroup;
|
||||
this.activeGroup = activeGroup;
|
||||
this.inventoryContents = cloneItems(inventoryContents);
|
||||
this.armorContents = cloneItems(armorContents);
|
||||
this.offHandItem = cloneItem(offHandItem);
|
||||
this.location = location == null ? null : location.clone();
|
||||
this.gameMode = gameMode;
|
||||
this.health = health;
|
||||
this.maxHealth = maxHealth;
|
||||
this.foodLevel = foodLevel;
|
||||
this.saturation = saturation;
|
||||
this.exhaustion = exhaustion;
|
||||
this.expLevel = expLevel;
|
||||
this.exp = exp;
|
||||
this.totalExperience = totalExperience;
|
||||
this.potionEffects = potionEffects == null ? List.of() : new ArrayList<>(potionEffects);
|
||||
this.flying = flying;
|
||||
this.allowFlight = allowFlight;
|
||||
this.flySpeed = flySpeed;
|
||||
this.walkSpeed = walkSpeed;
|
||||
this.fireTicks = fireTicks;
|
||||
}
|
||||
|
||||
public static ModeSession capture(Player player, ModeratorProfile profile, String previousGroup, String activeGroup) {
|
||||
double maxHealth = 20.0D;
|
||||
if (player.getAttribute(Attribute.MAX_HEALTH) != null) {
|
||||
maxHealth = player.getAttribute(Attribute.MAX_HEALTH).getBaseValue();
|
||||
}
|
||||
|
||||
return new ModeSession(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
profile,
|
||||
System.currentTimeMillis(),
|
||||
previousGroup,
|
||||
activeGroup,
|
||||
player.getInventory().getContents(),
|
||||
player.getInventory().getArmorContents(),
|
||||
player.getInventory().getItemInOffHand(),
|
||||
player.getLocation(),
|
||||
player.getGameMode(),
|
||||
player.getHealth(),
|
||||
maxHealth,
|
||||
player.getFoodLevel(),
|
||||
player.getSaturation(),
|
||||
player.getExhaustion(),
|
||||
player.getLevel(),
|
||||
player.getExp(),
|
||||
player.getTotalExperience(),
|
||||
player.getActivePotionEffects(),
|
||||
player.isFlying(),
|
||||
player.getAllowFlight(),
|
||||
player.getFlySpeed(),
|
||||
player.getWalkSpeed(),
|
||||
player.getFireTicks());
|
||||
}
|
||||
|
||||
public UUID getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public ModeratorProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public long getStartTimestamp() {
|
||||
return startTimestamp;
|
||||
}
|
||||
|
||||
public String getPreviousGroup() {
|
||||
return previousGroup;
|
||||
}
|
||||
|
||||
public String getActiveGroup() {
|
||||
return activeGroup;
|
||||
}
|
||||
|
||||
public ItemStack[] getInventoryContents() {
|
||||
return cloneItems(inventoryContents);
|
||||
}
|
||||
|
||||
public ItemStack[] getArmorContents() {
|
||||
return cloneItems(armorContents);
|
||||
}
|
||||
|
||||
public ItemStack getOffHandItem() {
|
||||
return cloneItem(offHandItem);
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location == null ? null : location.clone();
|
||||
}
|
||||
|
||||
public GameMode getGameMode() {
|
||||
return gameMode;
|
||||
}
|
||||
|
||||
public double getHealth() {
|
||||
return health;
|
||||
}
|
||||
|
||||
public double getMaxHealth() {
|
||||
return maxHealth;
|
||||
}
|
||||
|
||||
public int getFoodLevel() {
|
||||
return foodLevel;
|
||||
}
|
||||
|
||||
public float getSaturation() {
|
||||
return saturation;
|
||||
}
|
||||
|
||||
public float getExhaustion() {
|
||||
return exhaustion;
|
||||
}
|
||||
|
||||
public int getExpLevel() {
|
||||
return expLevel;
|
||||
}
|
||||
|
||||
public float getExp() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public int getTotalExperience() {
|
||||
return totalExperience;
|
||||
}
|
||||
|
||||
public Collection<PotionEffect> getPotionEffects() {
|
||||
return new ArrayList<>(potionEffects);
|
||||
}
|
||||
|
||||
public boolean isFlying() {
|
||||
return flying;
|
||||
}
|
||||
|
||||
public boolean isAllowFlight() {
|
||||
return allowFlight;
|
||||
}
|
||||
|
||||
public float getFlySpeed() {
|
||||
return flySpeed;
|
||||
}
|
||||
|
||||
public float getWalkSpeed() {
|
||||
return walkSpeed;
|
||||
}
|
||||
|
||||
public int getFireTicks() {
|
||||
return fireTicks;
|
||||
}
|
||||
|
||||
public long getDurationMillis() {
|
||||
return Math.max(0L, System.currentTimeMillis() - startTimestamp);
|
||||
}
|
||||
|
||||
private static ItemStack[] cloneItems(ItemStack[] source) {
|
||||
if (source == null) {
|
||||
return new ItemStack[0];
|
||||
}
|
||||
|
||||
ItemStack[] clone = new ItemStack[source.length];
|
||||
for (int i = 0; i < source.length; i++) {
|
||||
clone[i] = cloneItem(source[i]);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static ItemStack cloneItem(ItemStack item) {
|
||||
if (item == null || item.getType() == Material.AIR) {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
return item.clone();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.blz.adminmode.moderation;
|
||||
|
||||
import org.blz.adminmode.config.ConfigManager;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class ModeSessionLogger {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private final Plugin plugin;
|
||||
private final ConfigManager configManager;
|
||||
|
||||
public ModeSessionLogger(Plugin plugin, ConfigManager configManager) {
|
||||
this.plugin = plugin;
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
public void logEnter(ModeSession session) {
|
||||
writeLine("ENTER_" + modeSuffix(session), session, "-");
|
||||
}
|
||||
|
||||
public void logExit(ModeSession session) {
|
||||
writeLine("EXIT_" + modeSuffix(session), session, formatDuration(session.getDurationMillis()));
|
||||
}
|
||||
|
||||
public void logRecovered(ModeSession session) {
|
||||
writeLine("RECOVER_" + modeSuffix(session), session, formatDuration(session.getDurationMillis()));
|
||||
}
|
||||
|
||||
private void writeLine(String action, ModeSession session, String duration) {
|
||||
if (!configManager.isSessionLoggingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
File logFile = new File(plugin.getDataFolder(), configManager.getSessionLogFile());
|
||||
File parent = logFile.getParentFile();
|
||||
if (parent != null && !parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
String line = String.format(
|
||||
"[%s] %s | Player: %s | UUID: %s | Duration: %s%n",
|
||||
LocalDateTime.now().format(DATE_TIME_FORMATTER),
|
||||
action,
|
||||
session.getPlayerName(),
|
||||
session.getPlayerId(),
|
||||
duration);
|
||||
|
||||
try (FileWriter writer = new FileWriter(logFile, true)) {
|
||||
writer.write(line);
|
||||
} catch (IOException exception) {
|
||||
plugin.getLogger().warning("Не удалось записать сессионный лог: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String modeSuffix(ModeSession session) {
|
||||
return session.getProfile().isHardProfile() ? "HARD" : "SOFT";
|
||||
}
|
||||
|
||||
private String formatDuration(long millis) {
|
||||
Duration duration = Duration.ofMillis(Math.max(0L, millis));
|
||||
long hours = duration.toHours();
|
||||
long minutes = duration.toMinutesPart();
|
||||
long seconds = duration.toSecondsPart();
|
||||
|
||||
if (hours > 0) {
|
||||
return hours + "h " + minutes + "m " + seconds + "s";
|
||||
}
|
||||
if (minutes > 0) {
|
||||
return minutes + "m " + seconds + "s";
|
||||
}
|
||||
return seconds + "s";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.blz.adminmode.moderation;
|
||||
|
||||
public enum PlayerListAction {
|
||||
SPECTATE,
|
||||
TELEPORT,
|
||||
INVSEE
|
||||
}
|
||||
335
src/main/java/org/blz/adminmode/session/SessionStorage.java
Normal file
335
src/main/java/org/blz/adminmode/session/SessionStorage.java
Normal file
@@ -0,0 +1,335 @@
|
||||
package org.blz.adminmode.session;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.blz.adminmode.moderation.ModeSession;
|
||||
import org.blz.adminmode.moderation.ModeratorProfile;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.io.BukkitObjectInputStream;
|
||||
import org.bukkit.util.io.BukkitObjectOutputStream;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SessionStorage {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final File storageFile;
|
||||
private final Gson gson;
|
||||
|
||||
public SessionStorage(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.storageFile = new File(plugin.getDataFolder(), "sessions.json");
|
||||
this.gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
}
|
||||
|
||||
public Map<UUID, ModeSession> loadSessions() {
|
||||
Map<UUID, ModeSession> sessions = new LinkedHashMap<>();
|
||||
if (!storageFile.exists()) {
|
||||
return sessions;
|
||||
}
|
||||
|
||||
try (FileReader reader = new FileReader(storageFile)) {
|
||||
JsonElement root = JsonParser.parseReader(reader);
|
||||
if (!root.isJsonArray()) {
|
||||
return sessions;
|
||||
}
|
||||
|
||||
for (JsonElement element : root.getAsJsonArray()) {
|
||||
if (!element.isJsonObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ModeSession session = deserializeSession(element.getAsJsonObject());
|
||||
if (session != null) {
|
||||
sessions.put(session.getPlayerId(), session);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось загрузить sessions.json: " + exception.getMessage());
|
||||
}
|
||||
|
||||
return sessions;
|
||||
}
|
||||
|
||||
public void saveSessions(Map<UUID, ModeSession> sessions) {
|
||||
File parent = storageFile.getParentFile();
|
||||
if (parent != null && !parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
JsonArray array = new JsonArray();
|
||||
for (ModeSession session : sessions.values()) {
|
||||
array.add(serializeSession(session));
|
||||
}
|
||||
|
||||
try (FileWriter writer = new FileWriter(storageFile)) {
|
||||
writer.write(gson.toJson(array));
|
||||
} catch (IOException exception) {
|
||||
plugin.getLogger().warning("Не удалось сохранить sessions.json: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private JsonObject serializeSession(ModeSession session) {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("playerId", session.getPlayerId().toString());
|
||||
json.addProperty("playerName", session.getPlayerName());
|
||||
json.addProperty("profile", session.getProfile().getConfigKey());
|
||||
json.addProperty("startTimestamp", session.getStartTimestamp());
|
||||
json.addProperty("previousGroup", session.getPreviousGroup());
|
||||
json.addProperty("activeGroup", session.getActiveGroup());
|
||||
json.addProperty("inventory", serializeItemArray(session.getInventoryContents()));
|
||||
json.addProperty("armorContents", serializeItemArray(session.getArmorContents()));
|
||||
json.addProperty("offHand", serializeItem(session.getOffHandItem()));
|
||||
json.add("location", serializeLocation(session.getLocation()));
|
||||
json.addProperty("gameMode", session.getGameMode().name());
|
||||
json.addProperty("health", session.getHealth());
|
||||
json.addProperty("maxHealth", session.getMaxHealth());
|
||||
json.addProperty("foodLevel", session.getFoodLevel());
|
||||
json.addProperty("saturation", session.getSaturation());
|
||||
json.addProperty("exhaustion", session.getExhaustion());
|
||||
json.addProperty("expLevel", session.getExpLevel());
|
||||
json.addProperty("exp", session.getExp());
|
||||
json.addProperty("totalExperience", session.getTotalExperience());
|
||||
json.addProperty("potionEffects", serializePotionEffects(session.getPotionEffects()));
|
||||
json.addProperty("flying", session.isFlying());
|
||||
json.addProperty("allowFlight", session.isAllowFlight());
|
||||
json.addProperty("flySpeed", session.getFlySpeed());
|
||||
json.addProperty("walkSpeed", session.getWalkSpeed());
|
||||
json.addProperty("fireTicks", session.getFireTicks());
|
||||
return json;
|
||||
}
|
||||
|
||||
private ModeSession deserializeSession(JsonObject json) {
|
||||
try {
|
||||
UUID playerId = UUID.fromString(json.get("playerId").getAsString());
|
||||
String playerName = json.get("playerName").getAsString();
|
||||
ModeratorProfile profile = ModeratorProfile.fromConfigKey(json.get("profile").getAsString())
|
||||
.orElse(ModeratorProfile.SOFT_MODER);
|
||||
long startTimestamp = json.get("startTimestamp").getAsLong();
|
||||
String previousGroup = getString(json, "previousGroup");
|
||||
String activeGroup = getString(json, "activeGroup");
|
||||
ItemStack[] inventory = deserializeItemArray(getString(json, "inventory"));
|
||||
ItemStack[] armorContents = deserializeItemArray(getString(json, "armorContents"));
|
||||
ItemStack offHand = deserializeItem(getString(json, "offHand"));
|
||||
Location location = deserializeLocation(json.getAsJsonObject("location"));
|
||||
GameMode gameMode = GameMode.valueOf(json.get("gameMode").getAsString());
|
||||
double health = json.get("health").getAsDouble();
|
||||
double maxHealth = json.get("maxHealth").getAsDouble();
|
||||
int foodLevel = json.get("foodLevel").getAsInt();
|
||||
float saturation = json.get("saturation").getAsFloat();
|
||||
float exhaustion = json.get("exhaustion").getAsFloat();
|
||||
int expLevel = json.get("expLevel").getAsInt();
|
||||
float exp = json.get("exp").getAsFloat();
|
||||
int totalExperience = json.get("totalExperience").getAsInt();
|
||||
Collection<PotionEffect> potionEffects = deserializePotionEffects(getString(json, "potionEffects"));
|
||||
boolean flying = json.get("flying").getAsBoolean();
|
||||
boolean allowFlight = json.get("allowFlight").getAsBoolean();
|
||||
float flySpeed = json.get("flySpeed").getAsFloat();
|
||||
float walkSpeed = json.get("walkSpeed").getAsFloat();
|
||||
int fireTicks = json.get("fireTicks").getAsInt();
|
||||
|
||||
return new ModeSession(
|
||||
playerId,
|
||||
playerName,
|
||||
profile,
|
||||
startTimestamp,
|
||||
previousGroup,
|
||||
activeGroup,
|
||||
inventory,
|
||||
armorContents,
|
||||
offHand,
|
||||
location,
|
||||
gameMode,
|
||||
health,
|
||||
maxHealth,
|
||||
foodLevel,
|
||||
saturation,
|
||||
exhaustion,
|
||||
expLevel,
|
||||
exp,
|
||||
totalExperience,
|
||||
potionEffects,
|
||||
flying,
|
||||
allowFlight,
|
||||
flySpeed,
|
||||
walkSpeed,
|
||||
fireTicks);
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось десериализовать session entry: " + exception.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private JsonObject serializeLocation(Location location) {
|
||||
JsonObject json = new JsonObject();
|
||||
if (location == null || location.getWorld() == null) {
|
||||
return json;
|
||||
}
|
||||
|
||||
json.addProperty("world", location.getWorld().getName());
|
||||
json.addProperty("x", location.getX());
|
||||
json.addProperty("y", location.getY());
|
||||
json.addProperty("z", location.getZ());
|
||||
json.addProperty("yaw", location.getYaw());
|
||||
json.addProperty("pitch", location.getPitch());
|
||||
return json;
|
||||
}
|
||||
|
||||
private Location deserializeLocation(JsonObject json) {
|
||||
if (json == null || !json.has("world")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
World world = Bukkit.getWorld(json.get("world").getAsString());
|
||||
if (world == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Location(
|
||||
world,
|
||||
json.get("x").getAsDouble(),
|
||||
json.get("y").getAsDouble(),
|
||||
json.get("z").getAsDouble(),
|
||||
json.get("yaw").getAsFloat(),
|
||||
json.get("pitch").getAsFloat());
|
||||
}
|
||||
|
||||
private String getString(JsonObject json, String key) {
|
||||
return json.has(key) && !json.get(key).isJsonNull() ? json.get(key).getAsString() : "";
|
||||
}
|
||||
|
||||
private String serializeItemArray(ItemStack[] items) {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream)) {
|
||||
dataOutput.writeInt(items.length);
|
||||
for (ItemStack item : items) {
|
||||
dataOutput.writeObject(item);
|
||||
}
|
||||
}
|
||||
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось сериализовать item array: " + exception.getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private ItemStack[] deserializeItemArray(String data) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
return new ItemStack[0];
|
||||
}
|
||||
|
||||
try {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data));
|
||||
try (BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream)) {
|
||||
int length = dataInput.readInt();
|
||||
ItemStack[] items = new ItemStack[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
items[i] = (ItemStack) dataInput.readObject();
|
||||
}
|
||||
return items;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось десериализовать item array: " + exception.getMessage());
|
||||
return new ItemStack[0];
|
||||
}
|
||||
}
|
||||
|
||||
private String serializeItem(ItemStack item) {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream)) {
|
||||
dataOutput.writeObject(item);
|
||||
}
|
||||
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось сериализовать item: " + exception.getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private ItemStack deserializeItem(String data) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
try {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data));
|
||||
try (BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream)) {
|
||||
return (ItemStack) dataInput.readObject();
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось десериализовать item: " + exception.getMessage());
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
}
|
||||
|
||||
private String serializePotionEffects(Collection<PotionEffect> effects) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (PotionEffect effect : effects) {
|
||||
builder.append(effect.getType().getName()).append(':')
|
||||
.append(effect.getDuration()).append(':')
|
||||
.append(effect.getAmplifier()).append(':')
|
||||
.append(effect.isAmbient()).append(':')
|
||||
.append(effect.hasParticles()).append(':')
|
||||
.append(effect.hasIcon()).append(';');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private Collection<PotionEffect> deserializePotionEffects(String data) {
|
||||
List<PotionEffect> effects = new ArrayList<>();
|
||||
if (data == null || data.isEmpty()) {
|
||||
return effects;
|
||||
}
|
||||
|
||||
String[] effectStrings = data.split(";");
|
||||
for (String effectString : effectStrings) {
|
||||
if (effectString.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
String[] parts = effectString.split(":");
|
||||
PotionEffectType type = PotionEffectType.getByName(parts[0]);
|
||||
if (type == null) {
|
||||
continue;
|
||||
}
|
||||
effects.add(new PotionEffect(
|
||||
type,
|
||||
Integer.parseInt(parts[1]),
|
||||
Integer.parseInt(parts[2]),
|
||||
Boolean.parseBoolean(parts[3]),
|
||||
Boolean.parseBoolean(parts[4]),
|
||||
Boolean.parseBoolean(parts[5])));
|
||||
} catch (Exception exception) {
|
||||
plugin.getLogger().warning("Не удалось десериализовать potion effect: " + effectString);
|
||||
}
|
||||
}
|
||||
return effects;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user