first commit

This commit is contained in:
2026-03-26 11:24:40 +07:00
commit b439a42134
57 changed files with 3271 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/.DS_Store
/build

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

View File

@@ -0,0 +1,2 @@
#Fri Jan 16 17:25:37 NOVT 2026
gradle.version=9.0.0

Binary file not shown.

BIN
.gradle/file-system.probe Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
A0EDDFD9DCFA42B28E98A596AFD8237AB506F0D6CE2E10D64BEA80C21CB3949A

View File

10
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# Игнорируемые файлы по умолчанию
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# HTTP-клиент на основе редактора
/httpRequests/

6
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

13
.idea/easycode.ignore generated Normal file
View File

@@ -0,0 +1,13 @@
.idea
.vscode
node_modules/
dist/
vendor/
cache/
.*/
*.min.*
*.test.*
*.spec.*
*.bundle.*
*.bundle-min.*
*.log

17
.idea/gradle.xml generated Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="ms-21" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

8
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="ms-21" project-jdk-type="JavaSDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/AdminMode.main.iml" filepath="$PROJECT_DIR$/.idea/modules/AdminMode.main.iml" />
</modules>
</component>
</project>

14
.idea/modules/AdminMode.main.iml generated Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>PAPER</platformType>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>
<projectReimportVersion>1</projectReimportVersion>
</configuration>
</facet>
</component>
</module>

119
bin/main/config.yml Normal file
View File

@@ -0,0 +1,119 @@
# AdminMode Configuration
#РЕЖИМ АДМИНИСТРАТОРА
admin_mode:
# Включен ли режим администратора
enabled: true
# LuckPerms настройки
luckperms:
# Использовать ли LuckPerms (если false - используется стандартный OP)
enabled: true
# Группы которые выдаются при активации режима
# Можно указать несколько групп через запятую
groups:
- "moder_adm"
# Удалять ли группы при деактивации (если false - группы останутся)
remove_on_disable: true
# Настройки игрового режима
gamemode:
# Принудительно ставить спектатор (если false - креатив)
force_spectator: true
# Мобы игнорируют игрока в админ моде
mobs_ignore_player: true
# Разрешить полет в режиме выживания (в админ моде)
allow_flight_in_survival: true
# Режим бога (неуязвимость)
god_mode: true
# Кастомная скорость полета (0.1 = медленно, 0.2 = нормально, 0.5 = быстро, 1.0 = очень быстро)
custom_fly_speed: 0.2
# Кастомная скорость ходьбы (0.1 = медленно, 0.2 = нормально, 0.5 = быстро)
custom_walk_speed: 0.2
# Разрешенные режимы игры для переключения в админ моде
# Если список пустой - можно переключаться на любые режимы
# Доступные: SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR
allowed_gamemodes:
- "CREATIVE"
- "SPECTATOR"
# Ограничения в режиме администратора
restrictions:
# Запретить выбрасывание предметов
prevent_item_drop: true
# Запретить поднятие предметов
prevent_item_pickup: false
# Запретить перемещение предметов в контейнеры (сундуки, бочки и т.д.)
prevent_container_transfer: true
# Запретить использование предметов (кроме открытия контейнеров)
prevent_item_use: true
# Разрешить управление своим инвентарем
allow_own_inventory: true
# Разрешенные блоки для взаимодействия (открытие, использование)
allowed_blocks:
- "CHEST"
- "TRAPPED_CHEST"
- "BARREL"
- "SHULKER_BOX"
- "FURNACE"
- "BLAST_FURNACE"
- "SMOKER"
- "HOPPER"
- "DISPENSER"
- "DROPPER"
- "DOOR"
- "TRAPDOOR"
- "FENCE_GATE"
- "BUTTON"
- "LEVER"
- "ENDER_CHEST"
- "CRAFTING_TABLE"
- "ENCHANTING_TABLE"
- "ANVIL"
- "BREWING_STAND"
# Разрешенные блоки для постановки (whitelist)
# Если список пустой - можно ставить любые блоки
# Если список заполнен - можно ставить только эти блоки
allowed_place_blocks:
- "BARRIER"
- "LIGHT"
- "STRUCTURE_VOID"
- "STRUCTURE_BLOCK"
# Выдавать пресет блоков при входе в админ мод
give_preset_blocks: true
# Пресет блоков (формат: МАТЕРИАЛ:КОЛИЧЕСТВО)
preset_blocks:
- "BARRIER:64"
- "LIGHT:64"
- "STRUCTURE_VOID:64"
# Сообщения
messages:
enabled: "✓ Режим администратора активирован!"
enabled_subtitle: "Ваше состояние сохранено."
permissions_granted: "⚠ Вам выданы временные права администратора!"
disabled: "✓ Режим администратора отключен!"
disabled_subtitle: "Ваше состояние восстановлено."
permissions_removed: "✓ Права администратора отозваны!"
no_permission: "У вас нет прав для использования этой команды!"
item_drop_denied: "✖ В режиме администратора нельзя выбрасывать предметы!"
container_transfer_denied: "✖ В режиме администратора нельзя перемещать предметы в контейнеры!"
item_use_denied: "✖ В режиме администратора нельзя использовать предметы!"
block_place_denied: "✖ Вы можете ставить только разрешенные блоки!"
reloaded: "✓ Конфигурация AdminMode перезагружена!"

21
bin/main/plugin.yml Normal file
View File

@@ -0,0 +1,21 @@
name: AdminMode
version: '1.1.0'
main: org.blz.adminmode.AdminMode
api-version: '1.21'
author: BelzeBool, Kilka_v_HJIebe
description: Плагин режима администратора с сохранением состояния игрока
softdepend: [LuckPerms]
commands:
adminmode:
description: Режим администратора с сохранением состояния
usage: /<command>
aliases: [adm, am]
permissions:
adminmode.use:
description: Разрешает использовать режим администратора
default: op
adminmode.reload:
description: Разрешает перезагружать конфигурацию AdminMode
default: op

41
build.gradle Normal file
View File

@@ -0,0 +1,41 @@
plugins {
id 'java'
}
group = 'org.blz'
version = '1.21.11'
repositories {
mavenCentral()
maven {
name = 'papermc'
url = 'https://repo.papermc.io/repository/maven-public/'
}
}
dependencies {
compileOnly 'io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT'
compileOnly 'net.luckperms:api:5.4'
compileOnly 'com.google.code.gson:gson:2.10.1'
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
processResources {
def props = [version: version]
inputs.properties props
filteringCharset 'UTF-8'
filesMatching('plugin.yml') {
expand props
}
}
jar {
archiveFileName.set("${project.name}-${project.version}.jar")
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

251
gradlew vendored Executable file
View File

@@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

94
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,63 @@
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.listeners.AdminModeListener;
import org.blz.adminmode.listeners.GameModeChangeListener;
import org.blz.adminmode.listeners.MobTargetListener;
import org.bukkit.plugin.java.JavaPlugin;
public class AdminMode extends JavaPlugin {
private static AdminMode instance;
private ConfigManager configManager;
private AdminModeCommand adminModeCommand;
private AdminDialogService dialogService;
@Override
public void onEnable() {
instance = this;
saveDefaultConfig();
configManager = new ConfigManager(this);
adminModeCommand = new AdminModeCommand(this, configManager);
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),
this);
getServer().getPluginManager().registerEvents(new MobTargetListener(adminModeCommand, configManager), this);
getServer().getPluginManager().registerEvents(new GameModeChangeListener(adminModeCommand, configManager),
this);
// Load Pocket Dimension World
new org.blz.adminmode.utils.WorldLoader(this, "pocket_dimension").loadWorld();
// Register Abilities
belzeManager = 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 отключен!");
}
public static AdminMode getInstance() {
return instance;
}
public ConfigManager getConfigManager() {
return configManager;
}
public AdminModeCommand getAdminModeCommand() {
return adminModeCommand;
}
}

View File

@@ -0,0 +1,43 @@
package org.blz.adminmode.commands;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class AdminFixCommand implements CommandExecutor {
private final AdminModeCommand adminModeCommand;
public AdminFixCommand(AdminModeCommand adminModeCommand) {
this.adminModeCommand = adminModeCommand;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!sender.hasPermission("adminmode.admin")) {
sender.sendMessage(ChatColor.RED + "У вас нет прав для использования этой команды!");
return true;
}
if (args.length < 1) {
sender.sendMessage(ChatColor.RED + "Использование: /admfix <игрок>");
return true;
}
Player target = Bukkit.getPlayer(args[0]);
if (target == null) {
sender.sendMessage(ChatColor.RED + "Игрок не найден!");
return true;
}
adminModeCommand.forceDisableAbilities(target);
sender.sendMessage(ChatColor.GREEN + "Все способности отключены для игрока " + target.getName());
target.sendMessage(ChatColor.YELLOW + "⚠ Ваши способности были сброшены администратором");
return true;
}
}

View File

@@ -0,0 +1,632 @@
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 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;
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 AdminDialogService dialogService;
public AdminModeCommand(Plugin plugin, ConfigManager configManager) {
this.plugin = plugin;
this.configManager = configManager;
this.dataManager = new AdminModeDataManager(plugin);
loadAllStates();
}
public void setDialogService(AdminDialogService dialogService) {
this.dialogService = dialogService;
}
@Override
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());
return true;
}
configManager.loadConfig();
sender.sendMessage(ChatColor.GREEN + configManager.getMsgReloaded());
return true;
}
// Команда disable <игрок> - принудительно отключить способности
if (args.length > 0 && args[0].equalsIgnoreCase("disable")) {
if (!canUseHardActions(sender)) {
sender.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
return true;
}
if (args.length < 2) {
sender.sendMessage(ChatColor.RED + "Использование: /adminmode disable <игрок>");
return true;
}
forceDisableTarget(sender, args[1]);
return true;
}
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "Эта команда доступна только игрокам!");
return true;
}
Player player = (Player) sender;
if (!configManager.isEnabled()) {
player.sendMessage(ChatColor.RED + "Режим администратора отключен!");
return true;
}
if (args.length > 0 && args[0].equalsIgnoreCase("dialog")) {
if (!openRelevantDialog(player)) {
player.sendMessage(ChatColor.YELLOW + "Dialog-панель недоступна. Используйте /adminmode enable <soft|hard>.");
}
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>");
}
return true;
}
ModeratorProfile profile = ModeratorProfile.fromInput(args[1]).orElse(null);
if (profile == null) {
player.sendMessage(ChatColor.RED + "Неизвестный профиль. Доступно: soft, hard.");
return true;
}
toggleAdminMode(player, profile);
return true;
}
if (!isInAdminMode(player.getUniqueId()) && getAvailableProfiles(player).isEmpty()) {
player.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
return true;
}
toggleAdminMode(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) {
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;
}
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)) {
return false;
}
Player target = resolveOnlinePlayer(targetName, moderator);
if (target == null) {
return false;
}
moderator.teleport(target.getLocation());
moderator.sendMessage(ChatColor.GREEN + "✓ Вы телепортированы к игроку " + target.getName());
return true;
}
public boolean teleportPlayerHere(Player moderator, String targetName) {
if (!validateHardActionPlayer(moderator)) {
return false;
}
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;
}
public boolean forceDisableTarget(CommandSender sender, String targetName) {
if (!canUseHardActions(sender)) {
sender.sendMessage(ChatColor.RED + configManager.getMsgNoPermission());
return false;
}
Player target = resolveOnlinePlayer(targetName, sender);
if (target == null) {
return false;
}
forceDisableAbilities(target);
sender.sendMessage(ChatColor.GREEN + "Все способности отключены для игрока " + target.getName());
target.sendMessage(ChatColor.YELLOW + "⚠ Ваши способности были сброшены модератором");
return true;
}
private boolean openRelevantDialog(Player player) {
if (dialogService == null || !configManager.isDialogsEnabled()) {
return false;
}
if (isInAdminMode(player.getUniqueId())) {
return dialogService.openModerationPanel(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;
}
private boolean validateHardActionPlayer(Player moderator) {
if (!canUseHardActions(moderator)) {
moderator.sendMessage(ChatColor.RED + "Для этого действия нужен активный профиль Hard Moder.");
return false;
}
return true;
}
private Player resolveOnlinePlayer(String targetName, CommandSender sender) {
if (targetName == null || targetName.isBlank()) {
sender.sendMessage(ChatColor.RED + "Укажите ник игрока.");
return null;
}
Player target = Bukkit.getPlayerExact(targetName.trim());
if (target == null) {
target = Bukkit.getPlayer(targetName.trim());
}
if (target == null) {
sender.sendMessage(ChatColor.RED + "Игрок не найден!");
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;
}
}

View File

@@ -0,0 +1,257 @@
package org.blz.adminmode.config;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.blz.adminmode.moderation.ModeratorProfile;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin;
public class ConfigManager {
private final Plugin plugin;
private FileConfiguration config;
private boolean enabled;
private boolean useLuckPerms;
private Map<ModeratorProfile, List<String>> luckPermsGroups;
private boolean removeGroupsOnDisable;
private boolean dialogsEnabled;
private boolean preventItemDrop;
private boolean preventItemPickup;
private boolean preventContainerTransfer;
private boolean preventItemUse;
private boolean allowOwnInventory;
private List<String> allowedBlocks;
private List<String> allowedPlaceBlocks;
private boolean givePresetBlocks;
private List<String> presetBlocks;
private boolean forceSpectator;
private boolean mobsIgnorePlayer;
private boolean allowFlightInSurvival;
private boolean godMode;
private float customFlySpeed;
private float customWalkSpeed;
private List<String> allowedGameModes;
private String msgEnabled;
private String msgEnabledSubtitle;
private String msgPermissionsGranted;
private String msgDisabled;
private String msgDisabledSubtitle;
private String msgPermissionsRemoved;
private String msgNoPermission;
private String msgItemDropDenied;
private String msgContainerTransferDenied;
private String msgItemUseDenied;
private String msgBlockPlaceDenied;
private String msgReloaded;
public ConfigManager(Plugin plugin) {
this.plugin = plugin;
loadConfig();
}
public void loadConfig() {
plugin.reloadConfig();
config = plugin.getConfig();
enabled = config.getBoolean("admin_mode.enabled", true);
useLuckPerms = config.getBoolean("admin_mode.luckperms.enabled", true);
removeGroupsOnDisable = config.getBoolean("admin_mode.luckperms.remove_on_disable", true);
dialogsEnabled = config.getBoolean("admin_mode.dialogs.enabled", true);
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 (hardGroups.isEmpty()) {
hardGroups = legacyGroups.isEmpty() ? new ArrayList<>(softGroups) : new ArrayList<>(legacyGroups);
}
luckPermsGroups = new EnumMap<>(ModeratorProfile.class);
luckPermsGroups.put(ModeratorProfile.SOFT_MODER, new ArrayList<>(softGroups));
luckPermsGroups.put(ModeratorProfile.HARD_MODER, new ArrayList<>(hardGroups));
preventItemDrop = config.getBoolean("admin_mode.restrictions.prevent_item_drop", true);
preventItemPickup = config.getBoolean("admin_mode.restrictions.prevent_item_pickup", false);
preventContainerTransfer = config.getBoolean("admin_mode.restrictions.prevent_container_transfer", true);
preventItemUse = config.getBoolean("admin_mode.restrictions.prevent_item_use", true);
allowOwnInventory = config.getBoolean("admin_mode.restrictions.allow_own_inventory", true);
allowedBlocks = config.getStringList("admin_mode.restrictions.allowed_blocks");
allowedPlaceBlocks = config.getStringList("admin_mode.restrictions.allowed_place_blocks");
givePresetBlocks = config.getBoolean("admin_mode.restrictions.give_preset_blocks", true);
presetBlocks = config.getStringList("admin_mode.restrictions.preset_blocks");
forceSpectator = config.getBoolean("admin_mode.gamemode.force_spectator", false);
mobsIgnorePlayer = config.getBoolean("admin_mode.gamemode.mobs_ignore_player", true);
allowFlightInSurvival = config.getBoolean("admin_mode.gamemode.allow_flight_in_survival", true);
godMode = config.getBoolean("admin_mode.gamemode.god_mode", true);
customFlySpeed = (float) config.getDouble("admin_mode.gamemode.custom_fly_speed", 0.2);
customWalkSpeed = (float) config.getDouble("admin_mode.gamemode.custom_walk_speed", 0.2);
allowedGameModes = config.getStringList("admin_mode.gamemode.allowed_gamemodes");
msgEnabled = config.getString("admin_mode.messages.enabled", "✓ Режим администратора активирован!");
msgEnabledSubtitle = config.getString("admin_mode.messages.enabled_subtitle", "Ваше состояние сохранено.");
msgPermissionsGranted = config.getString("admin_mode.messages.permissions_granted",
"⚠ Вам выданы временные права администратора!");
msgDisabled = config.getString("admin_mode.messages.disabled", "✓ Режим администратора отключен!");
msgDisabledSubtitle = config.getString("admin_mode.messages.disabled_subtitle",
"Ваше состояние восстановлено.");
msgPermissionsRemoved = config.getString("admin_mode.messages.permissions_removed",
"✓ Права администратора отозваны!");
msgNoPermission = config.getString("admin_mode.messages.no_permission",
"У вас нет прав для использования этой команды!");
msgItemDropDenied = config.getString("admin_mode.messages.item_drop_denied",
"В режиме администратора нельзя выбрасывать предметы!");
msgContainerTransferDenied = config.getString("admin_mode.messages.container_transfer_denied",
"В режиме администратора нельзя перемещать предметы в контейнеры!");
msgItemUseDenied = config.getString("admin_mode.messages.item_use_denied",
"В режиме администратора нельзя использовать предметы!");
msgBlockPlaceDenied = config.getString("admin_mode.messages.block_place_denied",
"✖ Вы можете ставить только разрешенные блоки!");
msgReloaded = config.getString("admin_mode.messages.reloaded", "✓ Конфигурация AdminMode перезагружена!");
}
public boolean isEnabled() {
return enabled;
}
public boolean isUseLuckPerms() {
return useLuckPerms;
}
public List<String> getLuckPermsGroups() {
return getLuckPermsGroups(ModeratorProfile.SOFT_MODER);
}
public List<String> getLuckPermsGroups(ModeratorProfile profile) {
return new ArrayList<>(luckPermsGroups.getOrDefault(profile, List.of()));
}
public boolean isRemoveGroupsOnDisable() {
return removeGroupsOnDisable;
}
public boolean isDialogsEnabled() {
return dialogsEnabled;
}
public boolean isPreventItemDrop() {
return preventItemDrop;
}
public boolean isPreventItemPickup() {
return preventItemPickup;
}
public boolean isPreventContainerTransfer() {
return preventContainerTransfer;
}
public boolean isPreventItemUse() {
return preventItemUse;
}
public boolean isAllowOwnInventory() {
return allowOwnInventory;
}
public List<String> getAllowedBlocks() {
return allowedBlocks;
}
public List<String> getAllowedPlaceBlocks() {
return allowedPlaceBlocks;
}
public boolean isGivePresetBlocks() {
return givePresetBlocks;
}
public List<String> getPresetBlocks() {
return presetBlocks;
}
public boolean isForceSpectator() {
return forceSpectator;
}
public boolean isMobsIgnorePlayer() {
return mobsIgnorePlayer;
}
public boolean isAllowFlightInSurvival() {
return allowFlightInSurvival;
}
public boolean isGodMode() {
return godMode;
}
public float getCustomFlySpeed() {
return customFlySpeed;
}
public float getCustomWalkSpeed() {
return customWalkSpeed;
}
public List<String> getAllowedGameModes() {
return allowedGameModes;
}
public String getMsgEnabled() {
return msgEnabled;
}
public String getMsgEnabledSubtitle() {
return msgEnabledSubtitle;
}
public String getMsgPermissionsGranted() {
return msgPermissionsGranted;
}
public String getMsgDisabled() {
return msgDisabled;
}
public String getMsgDisabledSubtitle() {
return msgDisabledSubtitle;
}
public String getMsgPermissionsRemoved() {
return msgPermissionsRemoved;
}
public String getMsgNoPermission() {
return msgNoPermission;
}
public String getMsgItemDropDenied() {
return msgItemDropDenied;
}
public String getMsgContainerTransferDenied() {
return msgContainerTransferDenied;
}
public String getMsgItemUseDenied() {
return msgItemUseDenied;
}
public String getMsgBlockPlaceDenied() {
return msgBlockPlaceDenied;
}
public String getMsgReloaded() {
return msgReloaded;
}
}

View File

@@ -0,0 +1,314 @@
package org.blz.adminmode.data;
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.HashSet;
import java.util.Set;
import java.util.UUID;
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 com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class AdminModeDataManager {
private final Plugin plugin;
private final File dataFolder;
private final Gson gson;
public AdminModeDataManager(Plugin plugin) {
this.plugin = plugin;
this.dataFolder = new File(plugin.getDataFolder(), "adminmode");
this.gson = new GsonBuilder().setPrettyPrinting().create();
if (!dataFolder.exists()) {
dataFolder.mkdirs();
}
}
public void savePlayerState(UUID playerId, PlayerStateData state) {
File playerFile = new File(dataFolder, playerId.toString() + ".json");
try (FileWriter writer = new FileWriter(playerFile)) {
JsonObject json = new JsonObject();
json.addProperty("inventory", serializeItemArray(state.inventory));
json.addProperty("armorContents", serializeItemArray(state.armorContents));
json.addProperty("offHand", serializeItem(state.offHand));
JsonObject locationJson = new JsonObject();
locationJson.addProperty("world", state.location.getWorld().getName());
locationJson.addProperty("x", state.location.getX());
locationJson.addProperty("y", state.location.getY());
locationJson.addProperty("z", state.location.getZ());
locationJson.addProperty("yaw", state.location.getYaw());
locationJson.addProperty("pitch", state.location.getPitch());
json.add("location", locationJson);
json.addProperty("health", state.health);
json.addProperty("foodLevel", state.foodLevel);
json.addProperty("saturation", state.saturation);
json.addProperty("exhaustion", state.exhaustion);
json.addProperty("exp", state.exp);
json.addProperty("level", state.level);
json.addProperty("totalExperience", state.totalExperience);
json.addProperty("gameMode", state.gameMode.name());
json.addProperty("allowFlight", state.allowFlight);
json.addProperty("flying", state.flying);
json.addProperty("flySpeed", state.flySpeed);
json.addProperty("walkSpeed", state.walkSpeed);
json.addProperty("potionEffects", serializePotionEffects(state.potionEffects));
json.addProperty("fireTicks", state.fireTicks);
json.addProperty("profileKey", state.profileKey);
writer.write(gson.toJson(json));
plugin.getLogger().info("Сохранено состояние игрока: " + playerId);
} catch (IOException e) {
plugin.getLogger().severe("Не удалось сохранить состояние игрока " + playerId);
e.printStackTrace();
}
}
public PlayerStateData loadPlayerState(UUID playerId) {
File playerFile = new File(dataFolder, playerId.toString() + ".json");
if (!playerFile.exists()) {
return null;
}
try (FileReader reader = new FileReader(playerFile)) {
JsonObject json = JsonParser.parseReader(reader).getAsJsonObject();
PlayerStateData state = new PlayerStateData();
state.inventory = deserializeItemArray(json.get("inventory").getAsString());
state.armorContents = deserializeItemArray(json.get("armorContents").getAsString());
state.offHand = deserializeItem(json.get("offHand").getAsString());
JsonObject locationJson = json.getAsJsonObject("location");
World world = Bukkit.getWorld(locationJson.get("world").getAsString());
if (world != null) {
state.location = new Location(
world,
locationJson.get("x").getAsDouble(),
locationJson.get("y").getAsDouble(),
locationJson.get("z").getAsDouble(),
locationJson.get("yaw").getAsFloat(),
locationJson.get("pitch").getAsFloat()
);
}
state.health = json.get("health").getAsDouble();
state.foodLevel = json.get("foodLevel").getAsInt();
state.saturation = json.get("saturation").getAsFloat();
state.exhaustion = json.get("exhaustion").getAsFloat();
state.exp = json.get("exp").getAsFloat();
state.level = json.get("level").getAsInt();
state.totalExperience = json.get("totalExperience").getAsInt();
state.gameMode = GameMode.valueOf(json.get("gameMode").getAsString());
state.allowFlight = json.get("allowFlight").getAsBoolean();
state.flying = json.get("flying").getAsBoolean();
state.flySpeed = json.get("flySpeed").getAsFloat();
state.walkSpeed = json.get("walkSpeed").getAsFloat();
state.potionEffects = deserializePotionEffects(json.get("potionEffects").getAsString());
state.fireTicks = json.get("fireTicks").getAsInt();
if (json.has("profileKey") && !json.get("profileKey").isJsonNull()) {
state.profileKey = json.get("profileKey").getAsString();
}
plugin.getLogger().info("Загружено состояние игрока: " + playerId);
return state;
} catch (Exception e) {
plugin.getLogger().severe("Не удалось загрузить состояние игрока " + playerId);
e.printStackTrace();
return null;
}
}
public void deletePlayerState(UUID playerId) {
File playerFile = new File(dataFolder, playerId.toString() + ".json");
if (playerFile.exists()) {
if (playerFile.delete()) {
plugin.getLogger().info("Удалено состояние игрока: " + playerId);
} else {
plugin.getLogger().warning("Не удалось удалить файл состояния: " + playerId);
}
}
}
public boolean hasPlayerState(UUID playerId) {
File playerFile = new File(dataFolder, playerId.toString() + ".json");
return playerFile.exists();
}
public Set<UUID> getAllSavedStates() {
Set<UUID> states = new HashSet<>();
File[] files = dataFolder.listFiles((dir, name) -> name.endsWith(".json"));
if (files != null) {
for (File file : files) {
try {
String uuidStr = file.getName().replace(".json", "");
states.add(UUID.fromString(uuidStr));
} catch (IllegalArgumentException e) {
plugin.getLogger().warning("Некорректный файл состояния: " + file.getName());
}
}
}
return states;
}
private String serializeItemArray(ItemStack[] items) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
dataOutput.writeInt(items.length);
for (ItemStack item : items) {
dataOutput.writeObject(item);
}
dataOutput.close();
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
private ItemStack[] deserializeItemArray(String data) {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data));
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();
}
dataInput.close();
return items;
} catch (Exception e) {
e.printStackTrace();
return new ItemStack[0];
}
}
private String serializeItem(ItemStack item) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
dataOutput.writeObject(item);
dataOutput.close();
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
private ItemStack deserializeItem(String data) {
try {
if (data.isEmpty()) {
return new ItemStack(Material.AIR);
}
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data));
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
ItemStack item = (ItemStack) dataInput.readObject();
dataInput.close();
return item;
} catch (Exception e) {
e.printStackTrace();
return new ItemStack(Material.AIR);
}
}
private String serializePotionEffects(Collection<PotionEffect> effects) {
StringBuilder sb = new StringBuilder();
for (PotionEffect effect : effects) {
sb.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 sb.toString();
}
private Collection<PotionEffect> deserializePotionEffects(String data) {
Collection<PotionEffect> effects = new ArrayList<>();
if (data.isEmpty()) return effects;
String[] effectStrings = data.split(";");
for (String effectStr : effectStrings) {
if (effectStr.isEmpty()) continue;
try {
String[] parts = effectStr.split(":");
PotionEffectType type = PotionEffectType.getByName(parts[0]);
int duration = Integer.parseInt(parts[1]);
int amplifier = Integer.parseInt(parts[2]);
boolean ambient = Boolean.parseBoolean(parts[3]);
boolean particles = Boolean.parseBoolean(parts[4]);
boolean icon = Boolean.parseBoolean(parts[5]);
effects.add(new PotionEffect(type, duration, amplifier, ambient, particles, icon));
} catch (Exception e) {
plugin.getLogger().warning("Ошибка десериализации эффекта: " + effectStr);
}
}
return effects;
}
public static class PlayerStateData {
public ItemStack[] inventory;
public ItemStack[] armorContents;
public ItemStack offHand;
public Location location;
public double health;
public int foodLevel;
public float saturation;
public float exhaustion;
public float exp;
public int level;
public int totalExperience;
public GameMode gameMode;
public boolean allowFlight;
public boolean flying;
public float flySpeed;
public float walkSpeed;
public Collection<PotionEffect> potionEffects;
public int fireTicks;
public String profileKey;
}
}

View File

@@ -0,0 +1,297 @@
package org.blz.adminmode.dialog;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.data.dialog.DialogBase;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.action.DialogActionCallback;
import io.papermc.paper.registry.data.dialog.body.DialogBody;
import io.papermc.paper.registry.data.dialog.input.DialogInput;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import net.kyori.adventure.text.Component;
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.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class AdminDialogService {
private static final ClickCallback.Options CALLBACK_OPTIONS = ClickCallback.Options.builder()
.uses(1)
.lifetime(Duration.ofMinutes(5))
.build();
private final Plugin plugin;
private final ConfigManager configManager;
private final AdminModeCommand adminModeCommand;
public AdminDialogService(Plugin plugin, ConfigManager configManager, AdminModeCommand adminModeCommand) {
this.plugin = plugin;
this.configManager = configManager;
this.adminModeCommand = adminModeCommand;
}
public boolean openProfileSelectionDialog(Player player) {
if (!configManager.isDialogsEnabled()) {
return false;
}
List<ModeratorProfile> profiles = adminModeCommand.getAvailableProfiles(player);
if (profiles.isEmpty()) {
player.sendMessage(configManager.getMsgNoPermission());
return false;
}
if (profiles.size() == 1) {
adminModeCommand.toggleAdminMode(player, profiles.get(0));
return true;
}
try {
List<ActionButton> buttons = new ArrayList<>();
for (ModeratorProfile profile : profiles) {
buttons.add(actionButton(
profile.getDisplayName(),
profile.isHardProfile()
? "Полный режим модерации с сильными действиями"
: "Базовый режим модерации для повседневной работы",
(response, audience) -> withPlayer(audience, moderator -> {
adminModeCommand.toggleAdminMode(moderator, profile);
plugin.getServer().getScheduler().runTask(plugin, () -> {
if (adminModeCommand.isInAdminMode(moderator.getUniqueId())) {
openModerationPanel(moderator);
}
});
})));
}
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 — для телепортов и сильных действий."),
360)))
.inputs(List.of())
.build();
showDialog(player, base, DialogType.multiAction(buttons, closeButton(), 2));
return true;
} catch (Exception exception) {
plugin.getLogger().warning("Не удалось открыть dialog выбора профиля: " + exception.getMessage());
return false;
}
}
public boolean openModerationPanel(Player player) {
if (!configManager.isDialogsEnabled()) {
return false;
}
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
return false;
}
try {
UUID playerId = player.getUniqueId();
ModeratorProfile profile = adminModeCommand.getActiveProfile(playerId);
boolean hardProfile = profile != null && profile.isHardProfile();
List<ActionButton> buttons = new ArrayList<>();
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);
})));
}
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("Для действий по цели используйте поле с ником игрока."));
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(DialogInput.text(
"target_player",
320,
Component.text("Ник цели"),
true,
"",
16,
null)))
.build();
showDialog(player, base, DialogType.multiAction(buttons, closeButton(), 2));
return true;
} catch (Exception exception) {
plugin.getLogger().warning("Не удалось открыть moderation dialog: " + exception.getMessage());
return false;
}
}
public boolean openSpeedPanel(Player player) {
if (!configManager.isDialogsEnabled()) {
return false;
}
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
return false;
}
try {
float currentFly = clamp(player.getFlySpeed(), 0.1f, 1.0f);
float currentWalk = clamp(player.getWalkSpeed(), 0.1f, 1.0f);
ActionButton applyButton = actionButton(
"Применить",
"Сохранить выбранные значения скорости и бессмертия",
(response, audience) -> withPlayer(audience, moderator -> {
Float flySpeed = response.getFloat("fly_speed");
Float walkSpeed = response.getFloat("walk_speed");
Boolean godMode = response.getBoolean("god_mode");
adminModeCommand.applyAdminSettings(
moderator,
flySpeed == null ? currentFly : flySpeed,
walkSpeed == null ? currentWalk : walkSpeed,
godMode != null && godMode);
schedule(() -> openModerationPanel(moderator));
}));
ActionButton backButton = actionButton(
"Назад",
"Вернуться в основную панель модератора",
(response, audience) -> withPlayer(audience, moderator -> schedule(() -> openModerationPanel(moderator))));
DialogBase base = DialogBase.builder(Component.text("Настройка admin mode"))
.canCloseWithEscape(true)
.pause(false)
.afterAction(DialogBase.DialogAfterAction.CLOSE)
.body(List.of(DialogBody.plainMessage(Component.text(
"Настройте полет, скорость передвижения и режим бессмертия через новые элементы dialog API."),
360)))
.inputs(List.of(
DialogInput.numberRange(
"fly_speed",
320,
Component.text("Скорость полета"),
"%s: %s",
0.1f,
1.0f,
currentFly,
0.05f),
DialogInput.numberRange(
"walk_speed",
320,
Component.text("Скорость ходьбы"),
"%s: %s",
0.1f,
1.0f,
currentWalk,
0.05f),
DialogInput.bool(
"god_mode",
Component.text("Бессмертие"),
player.isInvulnerable(),
"true",
"false")))
.build();
showDialog(player, base, DialogType.multiAction(List.of(applyButton), backButton, 1));
return true;
} catch (Exception exception) {
plugin.getLogger().warning("Не удалось открыть dialog настройки скоростей: " + exception.getMessage());
return false;
}
}
private void showDialog(Player player, DialogBase base, io.papermc.paper.registry.data.dialog.type.DialogType type) {
Dialog dialog = Dialog.create(factory -> {
var builder = factory.empty();
builder.base(base);
builder.type(type);
});
player.showDialog(dialog);
}
private ActionButton actionButton(String label, String tooltip, DialogActionCallback callback) {
return ActionButton.builder(Component.text(label))
.tooltip(Component.text(tooltip))
.width(170)
.action(DialogAction.customClick(callback, CALLBACK_OPTIONS))
.build();
}
private ActionButton closeButton() {
return ActionButton.builder(Component.text("Закрыть"))
.width(140)
.build();
}
private void withPlayer(net.kyori.adventure.audience.Audience audience, java.util.function.Consumer<Player> consumer) {
if (audience instanceof Player player) {
consumer.accept(player);
}
}
private Component joinLines(List<Component> lines) {
Component result = Component.empty();
for (int i = 0; i < lines.size(); i++) {
if (i > 0) {
result = result.append(Component.newline());
}
result = result.append(lines.get(i));
}
return result;
}
private void schedule(Runnable runnable) {
plugin.getServer().getScheduler().runTask(plugin, runnable);
}
private String normalizeTarget(String targetName) {
return targetName == null ? "" : targetName.trim();
}
private float clamp(float value, float min, float max) {
return Math.max(min, Math.min(max, value));
}
}

View File

@@ -0,0 +1,129 @@
package org.blz.adminmode.listeners;
import org.blz.adminmode.commands.AdminModeCommand;
import org.blz.adminmode.config.ConfigManager;
import org.bukkit.ChatColor;
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.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;
public AdminModeListener(Plugin plugin, AdminModeCommand adminModeCommand, ConfigManager configManager) {
this.plugin = plugin;
this.adminModeCommand = adminModeCommand;
this.configManager = configManager;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onItemDrop(PlayerDropItemEvent e) {
Player player = e.getPlayer();
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) return;
if (!configManager.isPreventItemDrop()) return;
e.setCancelled(true);
player.sendMessage(ChatColor.RED + configManager.getMsgItemDropDenied());
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onItemPickup(EntityPickupItemEvent e) {
if (!(e.getEntity() instanceof Player)) return;
Player player = (Player) e.getEntity();
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) return;
if (!configManager.isPreventItemPickup()) return;
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onInventoryClick(InventoryClickEvent e) {
if (!(e.getWhoClicked() instanceof Player)) return;
Player player = (Player) e.getWhoClicked();
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) return;
if (!configManager.isPreventContainerTransfer()) return;
if (e.getClickedInventory() != null) {
if (e.getClickedInventory().getHolder() != null &&
!e.getClickedInventory().getHolder().equals(player)) {
e.setCancelled(true);
player.sendMessage(ChatColor.RED + configManager.getMsgContainerTransferDenied());
return;
}
}
if (e.isShiftClick()) {
if (e.getView().getTopInventory().getHolder() != null &&
!e.getView().getTopInventory().getHolder().equals(player)) {
e.setCancelled(true);
player.sendMessage(ChatColor.RED + configManager.getMsgContainerTransferDenied());
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerInteract(PlayerInteractEvent e) {
Player player = e.getPlayer();
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) return;
if (!configManager.isPreventItemUse()) return;
if (e.getAction() == org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) {
if (e.getClickedBlock() != null) {
Material blockType = e.getClickedBlock().getType();
List<String> allowedBlocks = configManager.getAllowedBlocks();
for (String allowed : allowedBlocks) {
if (blockType.name().contains(allowed.toUpperCase())) {
return;
}
}
}
}
if (e.getItem() != null && e.getItem().getType() != Material.AIR) {
e.setCancelled(true);
player.sendMessage(ChatColor.RED + configManager.getMsgItemUseDenied());
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onBlockPlace(BlockPlaceEvent e) {
Player player = e.getPlayer();
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) return;
List<String> allowedPlaceBlocks = configManager.getAllowedPlaceBlocks();
if (allowedPlaceBlocks.isEmpty()) return;
Material blockType = e.getBlock().getType();
for (String allowed : allowedPlaceBlocks) {
if (blockType.name().equalsIgnoreCase(allowed)) {
return;
}
}
e.setCancelled(true);
player.sendMessage(ChatColor.RED + configManager.getMsgBlockPlaceDenied());
}
}

View File

@@ -0,0 +1,86 @@
package org.blz.adminmode.listeners;
import org.blz.adminmode.commands.AdminModeCommand;
import org.blz.adminmode.config.ConfigManager;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerGameModeChangeEvent;
import org.bukkit.event.player.PlayerToggleFlightEvent;
import java.util.List;
public class GameModeChangeListener implements Listener {
private final AdminModeCommand adminModeCommand;
private final ConfigManager configManager;
public GameModeChangeListener(AdminModeCommand adminModeCommand, ConfigManager configManager) {
this.adminModeCommand = adminModeCommand;
this.configManager = configManager;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onGameModeChange(PlayerGameModeChangeEvent event) {
Player player = event.getPlayer();
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
return;
}
// КРИТИЧНО: Блокируем CREATIVE для всех в админ моде!
if (event.getNewGameMode() == GameMode.CREATIVE) {
event.setCancelled(true);
player.sendMessage(ChatColor.RED + "В режиме модератора КРЕАТИВ запрещен!");
player.sendMessage(ChatColor.GRAY + "Доступные режимы: SURVIVAL, SPECTATOR");
return;
}
List<String> allowedModes = configManager.getAllowedGameModes();
if (!allowedModes.isEmpty()) {
String newMode = event.getNewGameMode().name();
if (!allowedModes.contains(newMode)) {
event.setCancelled(true);
player.sendMessage(ChatColor.RED + "В админ моде можно переключаться только на разрешенные режимы!");
player.sendMessage(ChatColor.GRAY + "Разрешенные режимы: " + String.join(", ", allowedModes));
return;
}
}
// Включаем полет после смены режима, если это разрешено
if (configManager.isAllowFlightInSurvival()) {
player.getServer().getScheduler().runTaskLater(
player.getServer().getPluginManager().getPlugin("AdminMode"),
() -> {
player.setAllowFlight(true);
if (!player.isFlying()) {
player.setFlying(true);
}
},
1L);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onToggleFlight(PlayerToggleFlightEvent event) {
Player player = event.getPlayer();
if (!adminModeCommand.isInAdminMode(player.getUniqueId())) {
return;
}
if (!configManager.isAllowFlightInSurvival()) {
return;
}
// Всегда разрешаем полет в админ моде
if (!player.getAllowFlight()) {
player.setAllowFlight(true);
}
}
}

View File

@@ -0,0 +1,33 @@
package org.blz.adminmode.listeners;
import org.blz.adminmode.commands.AdminModeCommand;
import org.blz.adminmode.config.ConfigManager;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityTargetEvent;
public class MobTargetListener implements Listener {
private final AdminModeCommand adminModeCommand;
private final ConfigManager configManager;
public MobTargetListener(AdminModeCommand adminModeCommand, ConfigManager configManager) {
this.adminModeCommand = adminModeCommand;
this.configManager = configManager;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onMobTarget(EntityTargetEvent e) {
if (!configManager.isMobsIgnorePlayer()) return;
if (e.getTarget() instanceof Player) {
Player player = (Player) e.getTarget();
if (adminModeCommand.isInAdminMode(player.getUniqueId())) {
e.setCancelled(true);
}
}
}
}

View File

@@ -0,0 +1,413 @@
package org.blz.adminmode.managers;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.blz.adminmode.utils.ScreenEffects;
import org.bukkit.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
public class BelzeBoolManager implements Listener {
private final Plugin plugin;
private final String BELZEBOOL_NAME = "BelzeBool";
private final String POCKET_DIMENSION_WORLD = "pocket_dimension";
private final long COOLDOWN_MS = 16000; // 16 seconds
// Casting Mode State
private final Set<UUID> castingModePlayers = new HashSet<>();
private final Map<UUID, Integer> selectedSpellIndex = new HashMap<>();
private final List<String> spells = Arrays.asList("POCKET DIMENSION", "DEADLY KISS", "VANISH");
// Cooldowns: UUID -> SpellName -> Timestamp
private final Map<UUID, Map<String, Long>> cooldowns = new HashMap<>();
// Vanish State: UUID -> isVanished
private final Set<UUID> vanishedPlayers = new HashSet<>();
// Input Tracking
private final Map<UUID, List<Long>> rightClickTimestamps = new HashMap<>();
public BelzeBoolManager(Plugin plugin) {
this.plugin = plugin;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
startActionBarTask();
}
public boolean isBelzeBool(Player player) {
return player.getName().equalsIgnoreCase(BELZEBOOL_NAME);
}
// --- Casting Mode Logic ---
@EventHandler
public void onInteract(PlayerInteractEvent event) {
if (!isBelzeBool(event.getPlayer()))
return;
Player player = event.getPlayer();
// Casting Mode Toggle: Shift + Right Click x5 (only with empty hands)
if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
if (player.isSneaking() && event.getHand() == EquipmentSlot.HAND) {
// Only allow if hands are empty
ItemStack mainHand = player.getInventory().getItemInMainHand();
ItemStack offHand = player.getInventory().getItemInOffHand();
if ((mainHand == null || mainHand.getType() == Material.AIR) &&
(offHand == null || offHand.getType() == Material.AIR)) {
trackRightClick(player);
}
}
}
if (!castingModePlayers.contains(player.getUniqueId()))
return;
// Spell Cycle: Left Click (In Casting Mode)
if (event.getAction() == Action.LEFT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_BLOCK) {
if (event.getHand() == EquipmentSlot.HAND) { // Prevent double firing
event.setCancelled(true);
cycleSpell(player);
}
}
// Cast Spell: Right Click (In Casting Mode)
// If spell is VANISH, we can cast it on Air/Block too
if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
if (event.getHand() == EquipmentSlot.HAND) {
String spell = getSelectedSpell(player);
if (spell.equals("VANISH")) {
event.setCancelled(true);
castVanish(player);
}
}
}
}
@EventHandler
public void onEntityInteract(PlayerInteractEntityEvent event) {
if (!isBelzeBool(event.getPlayer()))
return;
if (event.getHand() != EquipmentSlot.HAND)
return;
Player player = event.getPlayer();
Entity target = event.getRightClicked();
if (castingModePlayers.contains(player.getUniqueId())) {
event.setCancelled(true);
castSpell(player, target);
}
}
private void trackRightClick(Player player) {
UUID uuid = player.getUniqueId();
List<Long> clicks = rightClickTimestamps.getOrDefault(uuid, new ArrayList<>());
long now = System.currentTimeMillis();
// Remove old clicks (>2s ago)
clicks.removeIf(time -> now - time > 2000);
clicks.add(now);
if (clicks.size() >= 5) {
toggleCastingMode(player);
clicks.clear();
}
rightClickTimestamps.put(uuid, clicks);
}
private void toggleCastingMode(Player player) {
UUID uuid = player.getUniqueId();
if (castingModePlayers.contains(uuid)) {
castingModePlayers.remove(uuid);
player.sendActionBar(Component.text("Casting Mode Deactivated", NamedTextColor.RED));
} else {
castingModePlayers.add(uuid);
selectedSpellIndex.putIfAbsent(uuid, 0);
player.sendActionBar(Component.text("Casting Mode Activated", NamedTextColor.GREEN));
}
player.playSound(player.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1f, 2f);
}
public void cycleSpell(Player player) {
UUID uuid = player.getUniqueId();
int index = selectedSpellIndex.getOrDefault(uuid, 0);
index = (index + 1) % spells.size();
selectedSpellIndex.put(uuid, index);
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.5f, 1f);
}
public void castCurrentSpell(Player player) {
if (!castingModePlayers.contains(player.getUniqueId())) {
// Optional: Auto-activate casting mode if key pressed?
// Or just ignore. User logic says key is for "ability".
// Logic in onInteract checks castingModePlayers.
// I will adhere to that.
return;
}
String spell = getSelectedSpell(player);
if (spell.equals("VANISH")) {
castVanish(player);
return;
}
// RayTrace for target
Entity target = rayTraceEntity(player, 20); // 20 blocks range
if (target != null) {
castSpell(player, target);
} else {
player.sendMessage(Component.text("No target found!", NamedTextColor.RED));
}
}
private Entity rayTraceEntity(Player player, double range) {
// Simple RayTrace
org.bukkit.util.RayTraceResult result = player.getWorld().rayTraceEntities(
player.getEyeLocation(),
player.getEyeLocation().getDirection(),
range,
entity -> entity != player && entity instanceof org.bukkit.entity.LivingEntity);
return result != null ? result.getHitEntity() : null;
}
private String getSelectedSpell(Player player) {
int index = selectedSpellIndex.getOrDefault(player.getUniqueId(), 0);
return spells.get(index);
}
private void startActionBarTask() {
new BukkitRunnable() {
@Override
public void run() {
for (UUID uuid : castingModePlayers) {
Player player = Bukkit.getPlayer(uuid);
if (player != null && player.isOnline()) {
String spell = getSelectedSpell(player);
long cooldown = getCooldownRemaining(uuid, spell);
Component text;
if (cooldown > 0) {
text = Component.text("< " + spell + " (" + (cooldown / 1000) + "s) >", NamedTextColor.RED,
TextDecoration.BOLD);
} else {
if (spell.equals("VANISH") && vanishedPlayers.contains(uuid)) {
text = Component.text("< " + spell + " (ACTIVE) >", NamedTextColor.AQUA,
TextDecoration.BOLD);
} else {
text = Component.text("< " + spell + " >", NamedTextColor.GOLD, TextDecoration.BOLD);
}
}
player.sendActionBar(text);
}
}
}
}.runTaskTimer(plugin, 0L, 5L);
}
private long getCooldownRemaining(UUID uuid, String spell) {
if (!cooldowns.containsKey(uuid))
return 0;
Long timestamp = cooldowns.get(uuid).get(spell);
if (timestamp == null)
return 0;
long remaining = (timestamp + COOLDOWN_MS) - System.currentTimeMillis();
return Math.max(0, remaining);
}
private void setCooldown(UUID uuid, String spell) {
cooldowns.computeIfAbsent(uuid, k -> new HashMap<>()).put(spell, System.currentTimeMillis());
}
private void castSpell(Player player, Entity target) {
String spell = getSelectedSpell(player);
// Vanish is self-targeted, but we allow clicking on entity to trigger it too if
// selected
if (spell.equals("VANISH")) {
castVanish(player);
return;
}
if (getCooldownRemaining(player.getUniqueId(), spell) > 0) {
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 0.5f);
return;
}
setCooldown(player.getUniqueId(), spell);
switch (spell) {
case "POCKET DIMENSION" -> castPocketDimension(player, target);
case "DEADLY KISS" -> castDeadlyKiss(player, target);
}
}
// --- Ability: Pocket Dimension ---
private void castPocketDimension(Player player, Entity target) {
World pocketWorld = Bukkit.getWorld(POCKET_DIMENSION_WORLD);
if (pocketWorld == null) {
player.sendMessage(Component.text("Error: Pocket Dimension world not loaded!", NamedTextColor.RED));
return;
}
// 1. Abduction Sequence (Visuals)
if (target instanceof Player targetP) {
targetP.addPotionEffect(new PotionEffect(PotionEffectType.DARKNESS, 140, 0));
targetP.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, 140, 4));
}
target.getWorld().playSound(target.getLocation(), Sound.ENTITY_WARDEN_HEARTBEAT, 1f, 0.5f);
target.getWorld().spawnParticle(Particle.SCULK_SOUL, target.getLocation(), 50, 0.5, 1, 0.5, 0.1);
Location originalLoc = target.getLocation();
// 2. Teleport after 5 seconds
new BukkitRunnable() {
@Override
public void run() {
if (!target.isValid())
return;
// Coordinates: 9 -56 17
Location dimLoc = new Location(pocketWorld, 9.5, -56, 17.5);
dimLoc.setYaw(originalLoc.getYaw());
dimLoc.setPitch(originalLoc.getPitch());
target.teleport(dimLoc);
target.getWorld().playSound(target.getLocation(), Sound.AMBIENT_BASALT_DELTAS_LOOP, 1f, 0.5f);
// 3. Return Sequence (Total 60s in Dimension)
// At 55s (1100 ticks from now), start ascension effects
new BukkitRunnable() {
@Override
public void run() {
if (!target.isValid())
return;
if (target instanceof Player targetP) {
targetP.addPotionEffect(new PotionEffect(PotionEffectType.LEVITATION, 100, 4)); // Fast up
targetP.playSound(targetP.getLocation(), Sound.ITEM_ELYTRA_FLYING, 1f, 0.5f);
}
}
}.runTaskLater(plugin, 1100L); // 55 seconds
// At 60s (1200 ticks), teleport back
new BukkitRunnable() {
@Override
public void run() {
if (target.isValid()) {
target.teleport(originalLoc);
target.getWorld().playSound(target.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1f, 1f);
// Remove effects
if (target instanceof Player targetP) {
targetP.removePotionEffect(PotionEffectType.LEVITATION);
targetP.removePotionEffect(PotionEffectType.DARKNESS);
}
}
}
}.runTaskLater(plugin, 1200L); // 60 seconds
}
}.runTaskLater(plugin, 100L); // 5 seconds delay
}
// --- Ability: Deadly Kiss ---
private void castDeadlyKiss(Player player, Entity target) {
player.playSound(player.getLocation(), Sound.ENTITY_GHAST_SCREAM, 1f, 0.5f);
if (target instanceof Player targetP) {
ScreenEffects.shake(plugin, targetP, 2.0f, 100);
}
target.getWorld().playSound(target.getLocation(), Sound.BLOCK_ANVIL_LAND, 1f, 0.5f);
target.getWorld().spawnParticle(Particle.SMOKE, target.getLocation(), 20, 0.5, 0.5, 0.5, 0.1);
Location originalLoc = target.getLocation();
// Slow Drag Down (10 blocks over 5 seconds)
new BukkitRunnable() {
int ticks = 0;
final int totalTicks = 100; // 5 seconds
final double distance = 10.0;
final double blocksPerTick = distance / totalTicks;
@Override
public void run() {
if (!target.isValid() || ticks >= totalTicks) {
this.cancel();
return;
}
Location currentLoc = target.getLocation();
// Ensure we don't drag them into void damage if world doesn't support it
if (currentLoc.getY() > -63) {
Location newLoc = currentLoc.subtract(0, blocksPerTick, 0);
target.teleport(newLoc);
}
if (ticks % 5 == 0) {
target.getWorld().spawnParticle(Particle.SMOKE, target.getLocation(), 2, 0.2, 0.5, 0.2, 0);
}
ticks++;
}
}.runTaskTimer(plugin, 0L, 1L);
}
// --- Ability: Vanish ---
private void castVanish(Player player) {
UUID uuid = player.getUniqueId();
if (vanishedPlayers.contains(uuid)) {
// Deactivate
vanishedPlayers.remove(uuid);
for (Player p : Bukkit.getOnlinePlayers()) {
p.showPlayer(plugin, player);
}
player.getWorld().playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1f, 1f);
player.getWorld().spawnParticle(Particle.WITCH, player.getLocation(), 50, 0.5, 1, 0.5, 0.1);
player.sendMessage(Component.text("You are now visible.", NamedTextColor.GREEN));
// COOLDOWN STARTS NOW
setCooldown(uuid, "VANISH");
} else {
// Activate
if (getCooldownRemaining(uuid, "VANISH") > 0) {
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 0.5f);
return;
}
vanishedPlayers.add(uuid);
for (Player p : Bukkit.getOnlinePlayers()) {
p.hidePlayer(plugin, player);
}
player.getWorld().playSound(player.getLocation(), Sound.ENTITY_GENERIC_EXTINGUISH_FIRE, 1f, 1f);
player.getWorld().spawnParticle(Particle.CLOUD, player.getLocation(), 50, 0.5, 1, 0.5, 0.1);
player.sendMessage(Component.text("You have vanished.", NamedTextColor.AQUA));
}
}
}

View File

@@ -0,0 +1,86 @@
package org.blz.adminmode.moderation;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
public enum ModeratorProfile {
SOFT_MODER("soft_moder", "Soft Moder", "adminmode.profile.soft"),
HARD_MODER("hard_moder", "Hard Moder", "adminmode.profile.hard");
private final String configKey;
private final String displayName;
private final String permission;
ModeratorProfile(String configKey, String displayName, String permission) {
this.configKey = configKey;
this.displayName = displayName;
this.permission = permission;
}
public String getConfigKey() {
return configKey;
}
public String getDisplayName() {
return displayName;
}
public String getPermission() {
return permission;
}
public boolean isHardProfile() {
return this == HARD_MODER;
}
public boolean isAvailableFor(Player player) {
return switch (this) {
case SOFT_MODER -> player.hasPermission("adminmode.use")
|| player.hasPermission(permission)
|| player.hasPermission(HARD_MODER.permission);
case HARD_MODER -> player.hasPermission(permission);
};
}
public static List<ModeratorProfile> availableFor(Player player) {
List<ModeratorProfile> profiles = new ArrayList<>();
for (ModeratorProfile profile : values()) {
if (profile.isAvailableFor(player)) {
profiles.add(profile);
}
}
return profiles;
}
public static Optional<ModeratorProfile> fromConfigKey(String key) {
if (key == null || key.isBlank()) {
return Optional.empty();
}
String normalized = key.toLowerCase(Locale.ROOT);
for (ModeratorProfile profile : values()) {
if (profile.configKey.equals(normalized)) {
return Optional.of(profile);
}
}
return Optional.empty();
}
public static Optional<ModeratorProfile> fromInput(String input) {
if (input == null || input.isBlank()) {
return Optional.empty();
}
String normalized = input.trim().toLowerCase(Locale.ROOT);
return switch (normalized) {
case "soft", "soft_moder", "soft-moder", "softmoder" -> Optional.of(SOFT_MODER);
case "hard", "hard_moder", "hard-moder", "hardmoder" -> Optional.of(HARD_MODER);
default -> Optional.empty();
};
}
}

View File

@@ -0,0 +1,53 @@
package org.blz.adminmode.utils;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class ScreenEffects {
private static final Random random = new Random();
/**
* Shakes the player's screen by jittering their view direction.
*
* @param plugin Plugin instance
* @param player Player to shake
* @param intensity Intensity of shake (1-15)
* @param durationTicks Duration in ticks
*/
public static void shake(Plugin plugin, Player player, float intensity, int durationTicks) {
final float originalYaw = player.getLocation().getYaw();
final float originalPitch = player.getLocation().getPitch();
final float maxShake = Math.min(intensity, 15f);
new BukkitRunnable() {
int ticksElapsed = 0;
@Override
public void run() {
if (ticksElapsed >= durationTicks || !player.isOnline()) {
Location loc = player.getLocation();
loc.setYaw(originalYaw);
loc.setPitch(originalPitch);
player.teleport(loc);
cancel();
return;
}
float shakeYaw = (random.nextFloat() - 0.5f) * 2 * maxShake;
float shakePitch = (random.nextFloat() - 0.5f) * 2 * maxShake;
Location loc = player.getLocation();
loc.setYaw(originalYaw + shakeYaw);
loc.setPitch(originalPitch + shakePitch);
player.teleport(loc);
ticksElapsed++;
}
}.runTaskTimer(plugin, 0L, 1L);
}
}

View File

@@ -0,0 +1,94 @@
package org.blz.adminmode.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.plugin.Plugin;
public class WorldLoader {
private final Plugin plugin;
private final String worldName;
public WorldLoader(Plugin plugin, String worldName) {
this.plugin = plugin;
this.worldName = worldName;
}
public void loadWorld() {
if (Bukkit.getWorld(worldName) != null) {
plugin.getLogger().info("Мир " + worldName + " уже загружен.");
return;
}
File worldFolder = new File(Bukkit.getWorldContainer(), worldName);
if (!worldFolder.exists()) {
plugin.getLogger().info("Мир " + worldName + " не найден. Распаковка из ресурсов...");
if (extractWorld(worldFolder)) {
plugin.getLogger().info("Мир успешно распакован.");
} else {
plugin.getLogger().severe("Не удалось распаковать мир " + worldName);
return;
}
}
plugin.getLogger().info("Загрузка мира " + worldName + "...");
WorldCreator creator = new WorldCreator(worldName);
creator.environment(World.Environment.NORMAL); // Or whatever environment it should be
World world = creator.createWorld();
if (world != null) {
// Apply specific rules for the pocket dimension
world.setGameRule(org.bukkit.GameRules.ADVANCE_TIME, false);
world.setGameRule(org.bukkit.GameRules.SPAWN_MOBS, false);
world.setTime(18000); // Midnight
plugin.getLogger().info("Мир " + worldName + " успешно загружен!");
} else {
plugin.getLogger().severe("Не удалось создать/загрузить мир " + worldName);
}
}
private boolean extractWorld(File destination) {
try (InputStream in = plugin.getResource("pocket_dimension.zip")) {
if (in == null) {
plugin.getLogger().severe("Resource pocket_dimension.zip not found in plugin JAR!");
return false;
}
try (ZipInputStream zipIn = new ZipInputStream(in)) {
ZipEntry entry = zipIn.getNextEntry();
while (entry != null) {
File file = new File(destination, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
} else {
// Ensure parent directories exist
if (file.getParentFile() != null) {
file.getParentFile().mkdirs();
}
try (FileOutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int len;
while ((len = zipIn.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
}
zipIn.closeEntry();
entry = zipIn.getNextEntry();
}
}
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}

View File

@@ -0,0 +1,124 @@
# AdminMode Configuration
#РЕЖИМ АДМИНИСТРАТОРА
admin_mode:
# Включен ли режим администратора
enabled: true
# LuckPerms настройки
luckperms:
# Использовать ли LuckPerms (если false - используется стандартный OP)
enabled: true
# Группы которые выдаются при активации режима
# Можно указать несколько групп через запятую
groups:
- "soft_moder"
profile_groups:
soft_moder:
- "soft_moder"
hard_moder:
- "hard_moder"
# Удалять ли группы при деактивации (если false - группы останутся)
remove_on_disable: true
dialogs:
enabled: true
# Настройки игрового режима
gamemode:
# Принудительно ставить спектатор (если false - survival)
force_spectator: false
# Мобы игнорируют игрока в админ моде
mobs_ignore_player: true
# Разрешить полет в режиме выживания (в админ моде)
allow_flight_in_survival: true
# Режим бога (неуязвимость)
god_mode: true
# Кастомная скорость полета (0.1 = медленно, 0.2 = нормально, 0.5 = быстро, 1.0 = очень быстро)
custom_fly_speed: 0.2
# Кастомная скорость ходьбы (0.1 = медленно, 0.2 = нормально, 0.5 = быстро)
custom_walk_speed: 0.2
# Разрешенные режимы игры для переключения в админ моде
# Если список пустой - можно переключаться на любые режимы
# Доступные: SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR
# ВАЖНО: CREATIVE убран - модеры не должны иметь доступ к креативу!
allowed_gamemodes:
- "SURVIVAL"
- "SPECTATOR"
# Ограничения в режиме администратора
restrictions:
# Запретить выбрасывание предметов
prevent_item_drop: false
# Запретить поднятие предметов
prevent_item_pickup: false
# Запретить перемещение предметов в контейнеры (сундуки, бочки и т.д.)
prevent_container_transfer: true
# Запретить использование предметов (кроме открытия контейнеров)
prevent_item_use: false
# Разрешить управление своим инвентарем
allow_own_inventory: true
# Разрешенные блоки для взаимодействия (открытие, использование)
allowed_blocks:
- "CHEST"
- "TRAPPED_CHEST"
- "BARREL"
- "SHULKER_BOX"
- "FURNACE"
- "BLAST_FURNACE"
- "SMOKER"
- "HOPPER"
- "DISPENSER"
- "DROPPER"
- "DOOR"
- "TRAPDOOR"
- "FENCE_GATE"
- "BUTTON"
- "LEVER"
- "ENDER_CHEST"
- "CRAFTING_TABLE"
- "ENCHANTING_TABLE"
- "ANVIL"
- "BREWING_STAND"
# Разрешенные блоки для постановки (whitelist)
# Если список пустой - можно ставить любые блоки
# Если список заполнен - можно ставить только эти блоки
allowed_place_blocks:
# Выдавать пресет блоков при входе в админ мод
give_preset_blocks: false
# Пресет блоков (формат: МАТЕРИАЛ:КОЛИЧЕСТВО)
preset_blocks:
- "BARRIER:64"
- "LIGHT:64"
- "STRUCTURE_VOID:64"
# Сообщения
messages:
enabled: "✓ Режим администратора активирован!"
enabled_subtitle: "Ваше состояние сохранено."
permissions_granted: "⚠ Вам выданы временные права администратора!"
disabled: "✓ Режим администратора отключен!"
disabled_subtitle: "Ваше состояние восстановлено."
permissions_removed: "✓ Права администратора отозваны!"
no_permission: "У вас нет прав для использования этой команды!"
item_drop_denied: "✖ В режиме администратора нельзя выбрасывать предметы!"
container_transfer_denied: "✖ В режиме администратора нельзя перемещать предметы в контейнеры!"
item_use_denied: "✖ В режиме администратора нельзя использовать предметы!"
block_place_denied: "✖ Вы можете ставить только разрешенные блоки!"
reloaded: "✓ Конфигурация AdminMode перезагружена!"

View File

@@ -0,0 +1,33 @@
name: AdminMode
version: '1.21.11'
main: org.blz.adminmode.AdminMode
api-version: '1.21'
author: BelzeBool, Kilka_v_HJIebe
description: Плагин режима администратора с сохранением состояния игрока
softdepend: [LuckPerms]
commands:
adminmode:
description: Режим администратора с сохранением состояния
usage: /<command>
aliases: [adm, am]
admfix:
description: Отключить все способности у игрока (полет, бессмертие)
usage: /<command> <игрок>
permissions:
adminmode.use:
description: Разрешает использовать режим администратора
default: op
adminmode.profile.soft:
description: Разрешает использовать профиль soft_moder
default: op
adminmode.profile.hard:
description: Разрешает использовать профиль hard_moder
default: op
adminmode.reload:
description: Разрешает перезагружать конфигурацию AdminMode
default: op
adminmode.admin:
description: Разрешает использовать сильные действия и админ команды (disable, admfix)
default: op

Binary file not shown.