1.0.0 SNAPSHOT
This commit is contained in:
1 parent
e2748c0d00
commit
acce87ea00
96 files changed
+4822
No files matched your search
@@ -19,3 +19,6 @@ gradle-app.setting
|
||||
.project
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
run
|
||||
.idea
|
||||
@@ -0,0 +1,84 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id("com.github.johnrengelman.shadow") version "7.0.0"
|
||||
id("xyz.jpenilla.run-paper") version "2.1.0"
|
||||
}
|
||||
|
||||
group = 'me.redtea'
|
||||
version = '1.0.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "spigotmc-repo"
|
||||
url = "https://hub.spigotmc.org/nexus/content/repositories/snapshots/"
|
||||
}
|
||||
maven {
|
||||
name = "sonatype"
|
||||
url = "https://oss.sonatype.org/content/groups/public/"
|
||||
}
|
||||
maven {
|
||||
name = "CodeMC"
|
||||
url = uri("https://repo.codemc.io/repository/maven-public/")
|
||||
}
|
||||
maven {
|
||||
name = "papermc-repo"
|
||||
url = "https://repo.papermc.io/repository/maven-public/"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.jetbrains:annotations:24.0.0'
|
||||
compileOnly 'org.projectlombok:lombok:1.18.26'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.26'
|
||||
compileOnly "com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT"
|
||||
implementation "me.mattstudios.utils:matt-framework:1.4"
|
||||
implementation 'de.tr7zw:item-nbt-api:2.11.3'
|
||||
implementation "net.kyori:adventure-api:4.14.0"
|
||||
implementation "net.kyori:adventure-platform-bukkit:4.3.0"
|
||||
implementation files("libs/MiniMessages.jar")
|
||||
compileOnly files("libs/nodropxforgebridge-1.1.jar")
|
||||
implementation "dev.triumphteam:triumph-gui:3.1.2"
|
||||
}
|
||||
|
||||
def targetJavaVersion = 8
|
||||
java {
|
||||
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
|
||||
sourceCompatibility = javaVersion
|
||||
targetCompatibility = javaVersion
|
||||
if (JavaVersion.current() < javaVersion) {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
|
||||
options.release = targetJavaVersion
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
def props = [version: version]
|
||||
inputs.properties props
|
||||
filteringCharset 'UTF-8'
|
||||
filesMatching('plugin.yml') {
|
||||
expand props
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
shadowJar {
|
||||
relocate 'de.tr7zw.changeme.nbtapi', 'me.redtea.nodropx.libs.tr7zw.nbt'
|
||||
relocate 'de.tr7zw.annotations', 'me.redtea.nodropx.libs.tr7zw.annotations'
|
||||
relocate 'me.mattstudios.mf', 'me.redtea.nodropx.libs.commands'
|
||||
relocate 'dev.triumphteam', 'me.redtea.nodropx.libs.gui'
|
||||
relocate 'org.checkerframework', 'me.redtea.nodropx.libs.message.oskarsmc.libs.checker'
|
||||
relocate 'org.intellij', 'me.redtea.nodropx.libs.intellij'
|
||||
relocate 'org.jetbrains', 'me.redtea.nodropx.libs.jetbrains'}
|
||||
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
minecraftVersion("1.16.5")
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
Vendored
BIN
Binary file not shown.
+6
@@ -0,0 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,245 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# 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/subprojects/plugins/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##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || 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=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# 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
|
||||
which java >/dev/null 2>&1 || 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
|
||||
|
||||
# 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=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=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 $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# 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" "$@"
|
||||
Vendored
+92
@@ -0,0 +1,92 @@
|
||||
@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
|
||||
|
||||
@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.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
: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
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
rootProject.name = 'NoDropX'
|
||||
include 'api'
|
||||
include 'bukkit'
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
package me.redtea.nodropx;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.mattstudios.mf.base.CommandManager;
|
||||
import me.redtea.nodropx.api.facade.NoDropAPI;
|
||||
import me.redtea.nodropx.api.facade.impl.NoDropXFacade;
|
||||
import me.redtea.nodropx.command.NoDropCommand;
|
||||
import me.redtea.nodropx.factory.item.NoDropItemFactory;
|
||||
import me.redtea.nodropx.factory.item.impl.NoDropItemFactoryImpl;
|
||||
import me.redtea.nodropx.factory.text.TextContext;
|
||||
import me.redtea.nodropx.factory.text.TextServicesFactory;
|
||||
import me.redtea.nodropx.factory.text.impl.TextServicesFactoryImpl;
|
||||
import me.redtea.nodropx.gui.facade.GuiFacade;
|
||||
import me.redtea.nodropx.gui.facade.impl.GuiFacadeImpl;
|
||||
import me.redtea.nodropx.gui.pages.StorageGui;
|
||||
import me.redtea.nodropx.libs.bstats.Metrics;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.MutableRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.Repo;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.yaml.YamlRepo;
|
||||
import me.redtea.nodropx.libs.message.container.Messages;
|
||||
import me.redtea.nodropx.listener.InventoryHandler;
|
||||
import me.redtea.nodropx.listener.PlayerHandler;
|
||||
import me.redtea.nodropx.model.cosmetic.impl.DisplayNameCosmetic;
|
||||
import me.redtea.nodropx.schema.ItemStackListSchema;
|
||||
import me.redtea.nodropx.service.cosmetic.CosmeticService;
|
||||
import me.redtea.nodropx.service.cosmetic.impl.CosmeticServiceImpl;
|
||||
import me.redtea.nodropx.service.event.EventService;
|
||||
import me.redtea.nodropx.service.event.impl.EventServiceImpl;
|
||||
import me.redtea.nodropx.service.nbt.NBTService;
|
||||
import me.redtea.nodropx.service.nbt.impl.Tr7zwNBTServiceImpl;
|
||||
import me.redtea.nodropx.service.nodrop.NoDropService;
|
||||
import me.redtea.nodropx.service.nodrop.impl.NoDropServiceImpl;
|
||||
import me.redtea.nodropx.service.respawn.RespawnService;
|
||||
import me.redtea.nodropx.service.respawn.impl.RespawnServiceImpl;
|
||||
import me.redtea.nodropx.service.storage.StorageService;
|
||||
import me.redtea.nodropx.service.storage.impl.StorageServiceImpl;
|
||||
import me.redtea.nodropxforgebridge.ApiProvider;
|
||||
import me.redtea.nodropxforgebridge.Nodropxforgebridge;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class NoDropX extends JavaPlugin {
|
||||
private static NoDropAPI noDropAPIInstance;
|
||||
@Getter
|
||||
private static NoDropX instance;
|
||||
|
||||
private Messages messages;
|
||||
private CosmeticService cosmeticService;
|
||||
@Getter
|
||||
private boolean supportHEX;
|
||||
@Getter
|
||||
private boolean supportKyoriLore;
|
||||
private MutableRepo<UUID, List<ItemStack>> pagesRepo;
|
||||
private Repo<String, List<Integer>> capacity;
|
||||
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
saveDefaultConfig();
|
||||
|
||||
TextServicesFactory textServicesFactory = new TextServicesFactoryImpl();
|
||||
TextContext textContext = textServicesFactory.create(this);
|
||||
supportHEX = textContext.isSupportHex();
|
||||
supportKyoriLore = textContext.isSupportKyori();
|
||||
messages = supportKyoriLore ? Messages.of(textContext.getMessages()) : Messages.legacy(textContext.getMessages());
|
||||
|
||||
cosmeticService = new CosmeticServiceImpl();
|
||||
cosmeticService.add(new DisplayNameCosmetic());
|
||||
cosmeticService.add(textContext.getLoreStrategy());
|
||||
cosmeticService.load(YamlConfiguration.loadConfiguration(textContext.getCosmetics()));
|
||||
|
||||
capacity = new YamlRepo<>(
|
||||
new File(getDataFolder(), "capacity.yml").toPath(),
|
||||
this,
|
||||
conf -> conf.getKeys(false).stream().collect(Collectors.toMap(
|
||||
value -> value,
|
||||
conf::getIntegerList))
|
||||
);
|
||||
|
||||
NBTService nbtService = new Tr7zwNBTServiceImpl();
|
||||
NoDropService noDropService = new NoDropServiceImpl(nbtService, cosmeticService);
|
||||
NoDropItemFactory noDropItemFactory = new NoDropItemFactoryImpl(noDropService);
|
||||
EventService eventService = new EventServiceImpl(noDropService);
|
||||
RespawnService respawnService = new RespawnServiceImpl(noDropService, capacity);
|
||||
pagesRepo = Repo.<UUID, List<ItemStack>>builder()
|
||||
.autoSave(1000 * 60 * 15L)
|
||||
.schema(new ItemStackListSchema(new File(getDataFolder(), "storage")))
|
||||
.plugin(this)
|
||||
.build();
|
||||
|
||||
|
||||
GuiFacade guiFacade = new GuiFacadeImpl(
|
||||
new StorageGui(messages, pagesRepo, noDropService, this)
|
||||
);
|
||||
StorageService storageService = new StorageServiceImpl(guiFacade, pagesRepo, noDropService);
|
||||
|
||||
NoDropAPI noDropAPI = new NoDropXFacade(noDropService, noDropItemFactory, storageService, capacity);
|
||||
noDropAPIInstance = noDropAPI;
|
||||
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(
|
||||
new InventoryHandler(eventService),
|
||||
this
|
||||
);
|
||||
Bukkit.getPluginManager().registerEvents(
|
||||
new PlayerHandler(respawnService, eventService, guiFacade),
|
||||
this
|
||||
);
|
||||
|
||||
Bukkit.getServicesManager().register(NoDropAPI.class,
|
||||
noDropAPI, this, ServicePriority.Normal);
|
||||
|
||||
CommandManager commandManager = new CommandManager(this);
|
||||
commandManager.getMessageHandler().register("cmd.no.permission", sender -> messages.get("noPerms").send(sender));
|
||||
commandManager.getMessageHandler().register("cmd.no.console", sender -> messages.get("noConsole").send(sender));
|
||||
commandManager.getMessageHandler().register("cmd.no.exists", sender -> messages.get("noExists").send(sender));
|
||||
commandManager.getMessageHandler().register("cmd.wrong.usage", sender -> messages.get("usage").send(sender));
|
||||
commandManager.getCompletionHandler().register("#materials", input -> Arrays.stream(Material.values()).map(Enum::name).collect(Collectors.toList()));
|
||||
commandManager.register(new NoDropCommand(messages, noDropAPI, guiFacade, this));
|
||||
bStatsInit();
|
||||
printCredits();
|
||||
|
||||
mod();
|
||||
}
|
||||
|
||||
//todo remove
|
||||
private void mod() {
|
||||
Nodropxforgebridge.noDropAPI = s -> getAPI().getCapacitySlots(s);
|
||||
}
|
||||
|
||||
|
||||
private void bStatsInit() {
|
||||
int pluginId = 19090;
|
||||
Metrics metrics = new Metrics(this, pluginId);
|
||||
}
|
||||
|
||||
|
||||
private void printCredits() {
|
||||
Bukkit.getLogger().info(ChatColor.GREEN + "************************************************************");
|
||||
Bukkit.getLogger().info(ChatColor.GREEN + "* " + ChatColor.YELLOW + "NoDropX version " + ChatColor.GOLD + getDescription().getVersion() + ChatColor.GRAY + " (uses CarcadeX v. 1.0.7)");
|
||||
Bukkit.getLogger().info(ChatColor.GREEN + "* " + ChatColor.YELLOW + "By " + ChatColor.RED + "itzRedTea");
|
||||
Bukkit.getLogger().info(ChatColor.GREEN + "* "+ ChatColor.YELLOW + "Detected bukkit version " + ChatColor.GOLD + Bukkit.getBukkitVersion());
|
||||
Bukkit.getLogger().info(ChatColor.GREEN + "* "+ ChatColor.YELLOW + "support HEX colors " + (supportHEX ? ChatColor.DARK_GREEN + "YES" : ChatColor.DARK_RED + "NO"));
|
||||
Bukkit.getLogger().info(ChatColor.GREEN + "* "+ ChatColor.YELLOW + "support Kyori as lore " + (supportKyoriLore ? ChatColor.DARK_GREEN + "YES" : ChatColor.DARK_RED + "NO"));
|
||||
Bukkit.getLogger().info(ChatColor.GREEN + "************************************************************");
|
||||
}
|
||||
|
||||
public void onReload() {
|
||||
pagesRepo.saveAll();
|
||||
capacity.reload();
|
||||
messages.reload(YamlConfiguration.loadConfiguration(new File(getDataFolder(), "messages.yml")));
|
||||
cosmeticService.load(YamlConfiguration.loadConfiguration(new File(getDataFolder(), "cosmetics.yml")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
pagesRepo.saveAll();
|
||||
}
|
||||
|
||||
public static NoDropAPI getAPI() {
|
||||
return noDropAPIInstance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package me.redtea.nodropx.api.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is called when item changes no drop status
|
||||
*/
|
||||
public class ChangeNoDropEvent extends PlayerEvent implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
@Getter
|
||||
private final boolean newValue;
|
||||
|
||||
@Getter
|
||||
@NotNull
|
||||
private final ItemStack item;
|
||||
|
||||
private boolean isCancelled;
|
||||
|
||||
public ChangeNoDropEvent(@NotNull Player who, boolean newValue, @NotNull ItemStack item) {
|
||||
super(who);
|
||||
this.newValue = newValue;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return isCancelled ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
isCancelled = cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package me.redtea.nodropx.api.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is called when player that has no drop item dead
|
||||
*/
|
||||
public class NoDropItemDropOnDeathEvent extends PlayerEvent {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
@Getter
|
||||
@NotNull
|
||||
private final ItemStack item;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean willDropped = false;
|
||||
|
||||
public NoDropItemDropOnDeathEvent(Player who, @NotNull ItemStack item) {
|
||||
super(who);
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package me.redtea.nodropx.api.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is called when no drop item is moved from the player's inventory
|
||||
*/
|
||||
public class NoDropItemThrownEvent extends PlayerEvent implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
@Getter
|
||||
@NotNull
|
||||
private final Reason reason;
|
||||
|
||||
@Getter
|
||||
@NotNull
|
||||
private final ItemStack item;
|
||||
|
||||
private boolean isCancelled;
|
||||
|
||||
public NoDropItemThrownEvent(Player who, @NotNull Reason reason, @NotNull ItemStack item) {
|
||||
super(who);
|
||||
this.reason = reason;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return isCancelled ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
isCancelled = cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public enum Reason {
|
||||
DROP_BY_PLAYER, MOVING_TO_OTHER_INVENTORY
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package me.redtea.nodropx.api.facade;
|
||||
|
||||
import me.redtea.nodropx.api.facade.manipulator.StorageManipulator;
|
||||
import me.redtea.nodropx.util.MaterialUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Facade for all plugin functions
|
||||
*/
|
||||
public interface NoDropAPI {
|
||||
/**
|
||||
* Set no drop to item
|
||||
* @param item the item who's no drop property are changing
|
||||
* @param isNoDrop will the item can drop from inventory on death?
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void setNoDrop(@NotNull ItemStack item, boolean isNoDrop);
|
||||
|
||||
/**
|
||||
* Check if item is no drop item
|
||||
* @param item the item being checked
|
||||
* @return true if item can not drop from inventory on death
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
boolean isNoDrop(@NotNull ItemStack item);
|
||||
|
||||
/**
|
||||
* Generate new no drop item by material and amount
|
||||
* @param material material of item
|
||||
* @param amount count of item
|
||||
* @return no drop item
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
ItemStack getNoDrop(@NotNull Material material, int amount);
|
||||
|
||||
|
||||
/**
|
||||
* Gives the entity no drop item
|
||||
* @param entity who will get no drop item
|
||||
* @param material type of item
|
||||
* @param amount amount of item
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void giveNoDrop(@NotNull HumanEntity entity, @NotNull Material material, int amount);
|
||||
|
||||
/**
|
||||
* returns an object for accessing the storage of a player with UUID playerUniqueId
|
||||
* @param playerUniqueId UUID of player
|
||||
* @return object for accessing the storage
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull StorageManipulator getStorageManipulator(@NotNull UUID playerUniqueId);
|
||||
|
||||
/**
|
||||
* returns the slot numbers that an no drop item with this material saves at death
|
||||
* @param material material to check
|
||||
* @return numbers of saved slots
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull Collection<Integer> getCapacitySlots(Material material);
|
||||
|
||||
/**
|
||||
* @see NoDropAPI#getCapacitySlots(Material)
|
||||
*/
|
||||
default @NotNull Collection<Integer> getCapacitySlots(String material) {
|
||||
return getCapacitySlots(MaterialUtils.getMaterialFromString(material));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NoDropAPI#getStorageManipulator(UUID)
|
||||
*/
|
||||
default @NotNull StorageManipulator getStorageManipulator(OfflinePlayer offlinePlayer) {
|
||||
return getStorageManipulator(offlinePlayer.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NoDropAPI#giveNoDrop(HumanEntity, Material, int)
|
||||
*/
|
||||
default void giveNoDrop(@NotNull HumanEntity entity, @NotNull String material, int amount) {
|
||||
giveNoDrop(entity, MaterialUtils.getMaterialFromString(material), amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NoDropAPI#giveNoDrop(HumanEntity, Material, int)
|
||||
*/
|
||||
default void giveNoDrop(@NotNull HumanEntity entity, @NotNull Material material) {
|
||||
giveNoDrop(entity, material, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NoDropAPI#giveNoDrop(HumanEntity, Material, int)
|
||||
*/
|
||||
default void giveNoDrop(@NotNull HumanEntity entity, @NotNull String material) {
|
||||
giveNoDrop(entity, material, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NoDropAPI#getNoDrop(Material, int)
|
||||
*/
|
||||
default @NotNull ItemStack getNoDrop(@NotNull String material, int amount) {
|
||||
return getNoDrop(MaterialUtils.getMaterialFromString(material), amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NoDropAPI#getNoDrop(Material, int)
|
||||
*/
|
||||
default @NotNull ItemStack getNoDrop(@NotNull Material material) {
|
||||
return getNoDrop(material, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NoDropAPI#getNoDrop(Material, int)
|
||||
*/
|
||||
default @NotNull ItemStack getNoDrop(@NotNull String material) {
|
||||
return getNoDrop(material, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package me.redtea.nodropx.api.facade.impl;
|
||||
|
||||
import me.redtea.nodropx.api.facade.NoDropAPI;
|
||||
import me.redtea.nodropx.api.facade.manipulator.StorageManipulator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.factory.item.NoDropItemFactory;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.Repo;
|
||||
import me.redtea.nodropx.service.nodrop.NoDropService;
|
||||
import me.redtea.nodropx.service.storage.StorageService;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class NoDropXFacade implements NoDropAPI {
|
||||
|
||||
private final NoDropService noDropService;
|
||||
private final NoDropItemFactory itemFactory;
|
||||
private final StorageService storageService;
|
||||
private final Repo<String, List<Integer>> capacity;
|
||||
|
||||
@Override
|
||||
public void setNoDrop(@NotNull ItemStack item, boolean isNoDrop) {
|
||||
noDropService.setNoDrop(item, isNoDrop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoDrop(@NotNull ItemStack item) {
|
||||
return noDropService.isNoDrop(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ItemStack getNoDrop(@NotNull Material material, int amount) {
|
||||
return itemFactory.createNoDrop(material, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void giveNoDrop(@NotNull HumanEntity entity, @NotNull Material material, int amount) {
|
||||
entity.getInventory().addItem(getNoDrop(material, amount));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StorageManipulator getStorageManipulator(@NotNull UUID playerUniqueId) {
|
||||
return new StorageManipulator() {
|
||||
@Override
|
||||
public void add(@NotNull ItemStack item) {
|
||||
storageService.addItem(playerUniqueId, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ItemStack item) {
|
||||
storageService.removeItem(playerUniqueId, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ItemStack> all() {
|
||||
return storageService.getItems(playerUniqueId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<Integer> getCapacitySlots(Material material) {
|
||||
return capacity.get(material.name()).orElse(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.redtea.nodropx.api.facade.manipulator;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StorageManipulator {
|
||||
/**
|
||||
* Adds no drop item to player storage
|
||||
* @param item - item that will add
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void add(@NotNull ItemStack item);
|
||||
/**
|
||||
* Remove no drop item to player storage
|
||||
* @param item - item that will remove
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void remove(@NotNull ItemStack item);
|
||||
|
||||
/**
|
||||
* @return items in storage
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull List<ItemStack> all();
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package me.redtea.nodropx.command;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.mattstudios.mf.annotations.*;
|
||||
import me.mattstudios.mf.base.CommandBase;
|
||||
import me.redtea.nodropx.NoDropX;
|
||||
import me.redtea.nodropx.api.facade.NoDropAPI;
|
||||
import me.redtea.nodropx.gui.facade.GuiFacade;
|
||||
import me.redtea.nodropx.libs.message.container.Messages;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Main command of plugin
|
||||
* <p>/nodrop - usage</p>
|
||||
* <p>/nodrop give [Player] [Material] [Amount (optional)]</p>
|
||||
*/
|
||||
@Command("nodrop")
|
||||
@RequiredArgsConstructor
|
||||
public class NoDropCommand extends CommandBase {
|
||||
private final Messages messages;
|
||||
private final NoDropAPI noDropAPI;
|
||||
private final GuiFacade guiFacade;
|
||||
private final NoDropX plugin;
|
||||
|
||||
@Default
|
||||
public void onDefault(final CommandSender sender) {
|
||||
messages.get("usage").send(sender);
|
||||
}
|
||||
|
||||
@SubCommand("give")
|
||||
@Permission("nodropx.give")
|
||||
@Completion({"#players", "#materials", "#range:1-64"})
|
||||
public void onGive(final CommandSender sender,
|
||||
final Player player,
|
||||
final String material,
|
||||
@Optional final Integer amount) {
|
||||
try {
|
||||
noDropAPI.giveNoDrop(player, material, amount == null ? 1 : amount);
|
||||
messages.get("give.success")
|
||||
.replaceAll("%item%", material)
|
||||
.replaceAll("%player%", player.getName())
|
||||
.replaceAll("%amount%", amount == null ? "0" : amount.toString())
|
||||
.send(sender);
|
||||
} catch (Throwable e) {
|
||||
messages.get("give.fail").send(sender);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SubCommand("reload")
|
||||
@Permission("nodropx.reload")
|
||||
public void onReload(final CommandSender sender) {
|
||||
long time = new Date().getTime();
|
||||
plugin.onReload();
|
||||
messages.get("reload").replaceAll("%time%", String.valueOf(new Date().getTime() - time)).send(sender);
|
||||
}
|
||||
|
||||
@SubCommand("gui")
|
||||
@Permission("nodropx.gui")
|
||||
public void onGui(final Player sender) {
|
||||
guiFacade.openNoDropStorage(sender);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.redtea.nodropx.factory.item;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface NoDropItemFactory {
|
||||
ItemStack createNoDrop(@NotNull Material material, int amount);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.redtea.nodropx.factory.item.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.factory.item.NoDropItemFactory;
|
||||
import me.redtea.nodropx.service.nodrop.NoDropService;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class NoDropItemFactoryImpl implements NoDropItemFactory {
|
||||
private final NoDropService noDropService;
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ItemStack createNoDrop(@NotNull Material material, int amount) {
|
||||
ItemStack result = new ItemStack(material, amount);
|
||||
noDropService.setNoDrop(result, true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package me.redtea.nodropx.factory.text;
|
||||
|
||||
import lombok.Data;
|
||||
import me.redtea.nodropx.model.cosmetic.CosmeticStrategy;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Data
|
||||
public class TextContext {
|
||||
private final CosmeticStrategy loreStrategy;
|
||||
private final File messages;
|
||||
private final File cosmetics;
|
||||
private final boolean supportHex;
|
||||
private final boolean supportKyori;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.redtea.nodropx.factory.text;
|
||||
|
||||
import me.redtea.nodropx.NoDropX;
|
||||
|
||||
public interface TextServicesFactory {
|
||||
TextContext create(NoDropX plugin);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package me.redtea.nodropx.factory.text.impl;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import me.redtea.nodropx.NoDropX;
|
||||
import me.redtea.nodropx.factory.text.TextContext;
|
||||
import me.redtea.nodropx.factory.text.TextServicesFactory;
|
||||
import me.redtea.nodropx.model.cosmetic.CosmeticStrategy;
|
||||
import me.redtea.nodropx.model.cosmetic.impl.LoreKyoriCosmetic;
|
||||
import me.redtea.nodropx.model.cosmetic.impl.LoreLegacyCosmetic;
|
||||
import me.redtea.nodropx.model.cosmetic.impl.LoreMD5Cosmetics;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class TextServicesFactoryImpl implements TextServicesFactory {
|
||||
private CosmeticStrategy cosmeticStrategy;
|
||||
private boolean isSupportKyoriLore;
|
||||
private boolean isSupportHEX = Integer.parseInt(Bukkit.getVersion().split("\\.")[1]) >= 16;
|
||||
|
||||
|
||||
public TextContext create(NoDropX plugin) {
|
||||
|
||||
try {
|
||||
Class<?> clazz = Class.forName("com.destroystokyo.paper.utils.PaperPluginLogger");
|
||||
isSupportKyoriLore = isSupportHEX;
|
||||
} catch (ClassNotFoundException e) {
|
||||
isSupportKyoriLore = false;
|
||||
}
|
||||
|
||||
if(plugin.getConfig().getBoolean("forceUseLegacy")) {
|
||||
isSupportHEX = false;
|
||||
isSupportKyoriLore = false;
|
||||
} else if(plugin.getConfig().getBoolean("forceUseMD5Colors")) {
|
||||
isSupportKyoriLore = false;
|
||||
}
|
||||
|
||||
|
||||
String folder = "legacy";
|
||||
if(isSupportKyoriLore) folder = "mm";
|
||||
else if(isSupportHEX) folder = "md5";
|
||||
|
||||
File messages = loadResource(plugin, folder, "messages.yml");
|
||||
File cosmetics = loadResource(plugin, folder, "cosmetics.yml");
|
||||
|
||||
if(!isSupportHEX) {
|
||||
plugin.getLogger().warning("This minecraft version did not support HEX");
|
||||
plugin.getLogger().warning("Using legacy message serializer");
|
||||
cosmeticStrategy = new LoreLegacyCosmetic();
|
||||
} else if(!isSupportKyoriLore) {
|
||||
plugin.getLogger().warning("This minecraft version did not support Kyori");
|
||||
plugin.getLogger().warning("Using legacy MD5 message serializer");
|
||||
cosmeticStrategy = new LoreMD5Cosmetics();
|
||||
} else cosmeticStrategy = new LoreKyoriCosmetic();
|
||||
|
||||
return new TextContext(cosmeticStrategy, messages, cosmetics, isSupportHEX, isSupportKyoriLore);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
private File loadResource(Plugin plugin, String folder, String resourceName) {
|
||||
File file = new File(plugin.getDataFolder(), resourceName);
|
||||
if(!file.exists()) {
|
||||
InputStream in = plugin.getResource("text/" + folder + "/" + resourceName);
|
||||
Files.copy(in, file.toPath());
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.redtea.nodropx.gui.facade;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface GuiFacade {
|
||||
void openNoDropStorage(Player player);
|
||||
void clearCache(Player player);
|
||||
void clearAllCache();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.redtea.nodropx.gui.facade.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.gui.facade.GuiFacade;
|
||||
import me.redtea.nodropx.gui.pages.StorageGui;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GuiFacadeImpl implements GuiFacade {
|
||||
private final StorageGui storageGui;
|
||||
|
||||
@Override
|
||||
public void openNoDropStorage(Player player) {
|
||||
storageGui.open(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache(Player player) {
|
||||
storageGui.clearCache(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllCache() {
|
||||
storageGui.clearAllCache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package me.redtea.nodropx.gui.pages;
|
||||
|
||||
import dev.triumphteam.gui.builder.item.ItemBuilder;
|
||||
import dev.triumphteam.gui.guis.Gui;
|
||||
import dev.triumphteam.gui.guis.GuiItem;
|
||||
import dev.triumphteam.gui.guis.ScrollingGui;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.MutableRepo;
|
||||
import me.redtea.nodropx.libs.message.container.Messages;
|
||||
import me.redtea.nodropx.service.nodrop.NoDropService;
|
||||
import me.redtea.nodropx.util.MaterialUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class StorageGui {
|
||||
private final Map<UUID, ScrollingGui> cache = new HashMap<>();
|
||||
private final Messages messages;
|
||||
private final MutableRepo<UUID, List<ItemStack>> pagesRepo;
|
||||
private final NoDropService noDropService;
|
||||
private final Plugin plugin;
|
||||
|
||||
public void open(Player player) {
|
||||
if(cache.containsKey(player.getUniqueId())) {
|
||||
cache.get(player.getUniqueId()).open(player);
|
||||
return;
|
||||
}
|
||||
|
||||
ScrollingGui gui = Gui.scrolling()
|
||||
.title(messages.get("gui.title").asComponent())
|
||||
.rows(6)
|
||||
.pageSize(45)
|
||||
.disableAllInteractions()
|
||||
.create();
|
||||
setFrame(gui);
|
||||
setButtons(gui);
|
||||
setAction(gui);
|
||||
setOnClose(gui);
|
||||
for(ItemStack item : pagesRepo.get(player.getUniqueId()).orElse(new ArrayList<>())) {
|
||||
gui.addItem(ItemBuilder.from(item).asGuiItem());
|
||||
}
|
||||
|
||||
gui.open(player);
|
||||
cache.put(player.getUniqueId(), gui);
|
||||
}
|
||||
|
||||
private void setOnClose(ScrollingGui gui) {
|
||||
gui.setCloseGuiAction(event -> pagesRepo.update(event.getPlayer().getUniqueId(), gui.getPageItems().stream()
|
||||
.map(GuiItem::getItemStack)
|
||||
.collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
private void setAction(ScrollingGui gui) {
|
||||
gui.setDefaultClickAction(event -> {
|
||||
if((event.getSlot() >= 45 && event.getSlot() <= 53) || event.getClickedInventory() instanceof PlayerInventory) {
|
||||
return;
|
||||
}
|
||||
if(event.getAction() == InventoryAction.PLACE_ALL && !event.getClick().isShiftClick()) {
|
||||
if(!noDropService.isNoDrop(event.getCursor())) {
|
||||
return;
|
||||
}
|
||||
ItemStack is = event.getCursor();
|
||||
event.setCursor(new ItemStack(Material.AIR));
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
gui.addItem(ItemBuilder.from(is).asGuiItem());
|
||||
gui.update();
|
||||
}
|
||||
}.runTask(plugin);
|
||||
} else if(event.getAction() == InventoryAction.SWAP_WITH_CURSOR && !event.getClick().isShiftClick()) {
|
||||
if(!noDropService.isNoDrop(event.getCursor())) {
|
||||
return;
|
||||
}
|
||||
ItemStack is = event.getCursor();
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
gui.addItem(ItemBuilder.from(is).asGuiItem());
|
||||
gui.update();
|
||||
}
|
||||
}.runTask(plugin);
|
||||
event.setCursor(new ItemStack(Material.AIR));
|
||||
} else if(event.getAction() == InventoryAction.PICKUP_ALL) {
|
||||
event.setCursor(event.getCurrentItem());
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
gui.removePageItem(event.getCurrentItem());
|
||||
gui.update();
|
||||
}
|
||||
}.runTask(plugin);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void setFrame(ScrollingGui gui) {
|
||||
for(int i = 1; i < 10; ++i) {
|
||||
gui.setItem(6, i, ItemBuilder.from(
|
||||
MaterialUtils.getMaterialFromString(messages.get("gui.frame.material").asUnparsedString())
|
||||
).name(messages.get("gui.frame.name").asComponent()).asGuiItem(event -> event.setCancelled(true)));
|
||||
}
|
||||
}
|
||||
|
||||
private void setButtons(ScrollingGui gui) {
|
||||
gui.setItem(6, 3, ItemBuilder.from(
|
||||
MaterialUtils.getMaterialFromString(messages.get("gui.prev.material").asUnparsedString())
|
||||
).name(messages.get("gui.prev.name").asComponent()).asGuiItem(event -> {
|
||||
gui.previous();
|
||||
event.setCancelled(true);
|
||||
}));
|
||||
gui.setItem(6, 7, ItemBuilder.from(
|
||||
MaterialUtils.getMaterialFromString(messages.get("gui.next.material").asUnparsedString())
|
||||
).name(messages.get("gui.next.name").asComponent()).asGuiItem(event -> {
|
||||
gui.next();
|
||||
event.setCancelled(true);
|
||||
}));
|
||||
}
|
||||
|
||||
public void clearCache(Player player) {
|
||||
cache.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void clearAllCache() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,881 @@
|
||||
/*
|
||||
* This Metrics class was auto-generated and can be copied into your project if you are
|
||||
* not using a build tool like Gradle or Maven for dependency management.
|
||||
*
|
||||
* IMPORTANT: You are not allowed to modify this class, except changing the package.
|
||||
*
|
||||
* Disallowed modifications include but are not limited to:
|
||||
* - Remove the option for users to opt-out
|
||||
* - Change the frequency for data submission
|
||||
* - Obfuscate the code (every obfuscator should allow you to make an exception for specific files)
|
||||
* - Reformat the code (if you use a linter, add an exception)
|
||||
*
|
||||
* Violations will result in a ban of your plugin and account from bStats.
|
||||
*/
|
||||
package me.redtea.nodropx.libs.bstats;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class Metrics {
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
private final MetricsBase metricsBase;
|
||||
|
||||
/**
|
||||
* Creates a new Metrics instance.
|
||||
*
|
||||
* @param plugin Your plugin instance.
|
||||
* @param serviceId The id of the service. It can be found at <a
|
||||
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
||||
*/
|
||||
public Metrics(JavaPlugin plugin, int serviceId) {
|
||||
this.plugin = plugin;
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
if (!config.isSet("serverUuid")) {
|
||||
config.addDefault("enabled", true);
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
config.addDefault("logFailedRequests", false);
|
||||
config.addDefault("logSentData", false);
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
// Inform the server owners about bStats
|
||||
config
|
||||
.options()
|
||||
.header(
|
||||
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
|
||||
+ "many people use their plugin and their total player count. It's recommended to keep bStats\n"
|
||||
+ "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
|
||||
+ "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
|
||||
+ "anonymous.")
|
||||
.copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
// Load the data
|
||||
boolean enabled = config.getBoolean("enabled", true);
|
||||
String serverUUID = config.getString("serverUuid");
|
||||
boolean logErrors = config.getBoolean("logFailedRequests", false);
|
||||
boolean logSentData = config.getBoolean("logSentData", false);
|
||||
boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
metricsBase =
|
||||
new MetricsBase(
|
||||
"bukkit",
|
||||
serverUUID,
|
||||
serviceId,
|
||||
enabled,
|
||||
this::appendPlatformData,
|
||||
this::appendServiceData,
|
||||
submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
|
||||
plugin::isEnabled,
|
||||
(message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
|
||||
(message) -> this.plugin.getLogger().log(Level.INFO, message),
|
||||
logErrors,
|
||||
logSentData,
|
||||
logResponseStatusText);
|
||||
}
|
||||
|
||||
/** Shuts down the underlying scheduler service. */
|
||||
public void shutdown() {
|
||||
metricsBase.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
metricsBase.addCustomChart(chart);
|
||||
}
|
||||
|
||||
private void appendPlatformData(JsonObjectBuilder builder) {
|
||||
builder.appendField("playerAmount", getPlayerAmount());
|
||||
builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0);
|
||||
builder.appendField("bukkitVersion", Bukkit.getVersion());
|
||||
builder.appendField("bukkitName", Bukkit.getName());
|
||||
builder.appendField("javaVersion", System.getProperty("java.version"));
|
||||
builder.appendField("osName", System.getProperty("os.name"));
|
||||
builder.appendField("osArch", System.getProperty("os.arch"));
|
||||
builder.appendField("osVersion", System.getProperty("os.version"));
|
||||
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
private void appendServiceData(JsonObjectBuilder builder) {
|
||||
builder.appendField("pluginVersion", plugin.getDescription().getVersion());
|
||||
}
|
||||
|
||||
private int getPlayerAmount() {
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed from an array to a collection,
|
||||
// This fixes java.lang.NoSuchMethodError:
|
||||
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
return onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
// Just use the new method if the reflection failed
|
||||
return Bukkit.getOnlinePlayers().size();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetricsBase {
|
||||
|
||||
/** The version of the Metrics class. */
|
||||
public static final String METRICS_VERSION = "3.0.2";
|
||||
|
||||
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s";
|
||||
|
||||
private final ScheduledExecutorService scheduler;
|
||||
|
||||
private final String platform;
|
||||
|
||||
private final String serverUuid;
|
||||
|
||||
private final int serviceId;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendPlatformDataConsumer;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendServiceDataConsumer;
|
||||
|
||||
private final Consumer<Runnable> submitTaskConsumer;
|
||||
|
||||
private final Supplier<Boolean> checkServiceEnabledSupplier;
|
||||
|
||||
private final BiConsumer<String, Throwable> errorLogger;
|
||||
|
||||
private final Consumer<String> infoLogger;
|
||||
|
||||
private final boolean logErrors;
|
||||
|
||||
private final boolean logSentData;
|
||||
|
||||
private final boolean logResponseStatusText;
|
||||
|
||||
private final Set<CustomChart> customCharts = new HashSet<>();
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
/**
|
||||
* Creates a new MetricsBase class instance.
|
||||
*
|
||||
* @param platform The platform of the service.
|
||||
* @param serviceId The id of the service.
|
||||
* @param serverUuid The server uuid.
|
||||
* @param enabled Whether or not data sending is enabled.
|
||||
* @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all platform-specific data.
|
||||
* @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all service-specific data.
|
||||
* @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be
|
||||
* used to delegate the data collection to a another thread to prevent errors caused by
|
||||
* concurrency. Can be {@code null}.
|
||||
* @param checkServiceEnabledSupplier A supplier to check if the service is still enabled.
|
||||
* @param errorLogger A consumer that accepts log message and an error.
|
||||
* @param infoLogger A consumer that accepts info log messages.
|
||||
* @param logErrors Whether or not errors should be logged.
|
||||
* @param logSentData Whether or not the sent data should be logged.
|
||||
* @param logResponseStatusText Whether or not the response status text should be logged.
|
||||
*/
|
||||
public MetricsBase(
|
||||
String platform,
|
||||
String serverUuid,
|
||||
int serviceId,
|
||||
boolean enabled,
|
||||
Consumer<JsonObjectBuilder> appendPlatformDataConsumer,
|
||||
Consumer<JsonObjectBuilder> appendServiceDataConsumer,
|
||||
Consumer<Runnable> submitTaskConsumer,
|
||||
Supplier<Boolean> checkServiceEnabledSupplier,
|
||||
BiConsumer<String, Throwable> errorLogger,
|
||||
Consumer<String> infoLogger,
|
||||
boolean logErrors,
|
||||
boolean logSentData,
|
||||
boolean logResponseStatusText) {
|
||||
ScheduledThreadPoolExecutor scheduler =
|
||||
new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics"));
|
||||
// We want delayed tasks (non-periodic) that will execute in the future to be
|
||||
// cancelled when the scheduler is shutdown.
|
||||
// Otherwise, we risk preventing the server from shutting down even when
|
||||
// MetricsBase#shutdown() is called
|
||||
scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
this.scheduler = scheduler;
|
||||
this.platform = platform;
|
||||
this.serverUuid = serverUuid;
|
||||
this.serviceId = serviceId;
|
||||
this.enabled = enabled;
|
||||
this.appendPlatformDataConsumer = appendPlatformDataConsumer;
|
||||
this.appendServiceDataConsumer = appendServiceDataConsumer;
|
||||
this.submitTaskConsumer = submitTaskConsumer;
|
||||
this.checkServiceEnabledSupplier = checkServiceEnabledSupplier;
|
||||
this.errorLogger = errorLogger;
|
||||
this.infoLogger = infoLogger;
|
||||
this.logErrors = logErrors;
|
||||
this.logSentData = logSentData;
|
||||
this.logResponseStatusText = logResponseStatusText;
|
||||
checkRelocation();
|
||||
if (enabled) {
|
||||
// WARNING: Removing the option to opt-out will get your plugin banned from
|
||||
// bStats
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
this.customCharts.add(chart);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
private void startSubmitting() {
|
||||
final Runnable submitTask =
|
||||
() -> {
|
||||
if (!enabled || !checkServiceEnabledSupplier.get()) {
|
||||
// Submitting data or service is disabled
|
||||
scheduler.shutdown();
|
||||
return;
|
||||
}
|
||||
if (submitTaskConsumer != null) {
|
||||
submitTaskConsumer.accept(this::submitData);
|
||||
} else {
|
||||
this.submitData();
|
||||
}
|
||||
};
|
||||
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven
|
||||
// distribution of requests on the
|
||||
// bStats backend. To circumvent this problem, we introduce some randomness into
|
||||
// the initial and second delay.
|
||||
// WARNING: You must not modify and part of this Metrics class, including the
|
||||
// submit delay or frequency!
|
||||
// WARNING: Modifying this code will get your plugin banned on bStats. Just
|
||||
// don't do it!
|
||||
long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
|
||||
long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
|
||||
scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
|
||||
scheduler.scheduleAtFixedRate(
|
||||
submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void submitData() {
|
||||
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
|
||||
appendPlatformDataConsumer.accept(baseJsonBuilder);
|
||||
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
|
||||
appendServiceDataConsumer.accept(serviceJsonBuilder);
|
||||
JsonObjectBuilder.JsonObject[] chartData =
|
||||
customCharts.stream()
|
||||
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(JsonObjectBuilder.JsonObject[]::new);
|
||||
serviceJsonBuilder.appendField("id", serviceId);
|
||||
serviceJsonBuilder.appendField("customCharts", chartData);
|
||||
baseJsonBuilder.appendField("service", serviceJsonBuilder.build());
|
||||
baseJsonBuilder.appendField("serverUUID", serverUuid);
|
||||
baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION);
|
||||
JsonObjectBuilder.JsonObject data = baseJsonBuilder.build();
|
||||
scheduler.execute(
|
||||
() -> {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Could not submit bStats metrics data", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendData(JsonObjectBuilder.JsonObject data) throws Exception {
|
||||
if (logSentData) {
|
||||
infoLogger.accept("Sent bStats metrics data: " + data.toString());
|
||||
}
|
||||
String url = String.format(REPORT_URL, platform);
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip");
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", "Metrics-Service/1");
|
||||
connection.setDoOutput(true);
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader =
|
||||
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
if (logResponseStatusText) {
|
||||
infoLogger.accept("Sent data to bStats and received response: " + builder);
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks that the class was properly relocated. */
|
||||
private void checkRelocation() {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null
|
||||
|| !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this
|
||||
// little "trick" ... :D
|
||||
final String defaultPackage =
|
||||
new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
|
||||
final String examplePackage =
|
||||
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure no one just copy & pastes the example and uses the wrong
|
||||
// package names
|
||||
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|
||||
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given string.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped string.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()});
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class CustomChart {
|
||||
|
||||
private final String chartId;
|
||||
|
||||
protected CustomChart(String chartId) {
|
||||
if (chartId == null) {
|
||||
throw new IllegalArgumentException("chartId must not be null");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
public JsonObjectBuilder.JsonObject getRequestJsonObject(
|
||||
BiConsumer<String, Throwable> errorLogger, boolean logErrors) {
|
||||
JsonObjectBuilder builder = new JsonObjectBuilder();
|
||||
builder.appendField("chartId", chartId);
|
||||
try {
|
||||
JsonObjectBuilder.JsonObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
builder.appendField("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception;
|
||||
}
|
||||
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extremely simple JSON builder.
|
||||
*
|
||||
* <p>While this class is neither feature-rich nor the most performant one, it's sufficient enough
|
||||
* for its use-case.
|
||||
*/
|
||||
public static class JsonObjectBuilder {
|
||||
|
||||
private StringBuilder builder = new StringBuilder();
|
||||
|
||||
private boolean hasAtLeastOneField = false;
|
||||
|
||||
public JsonObjectBuilder() {
|
||||
builder.append("{");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a null field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendNull(String key) {
|
||||
appendFieldUnescaped(key, "null");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("JSON value must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, "\"" + escape(value) + "\"");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int value) {
|
||||
appendFieldUnescaped(key, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param object The object.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject object) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("JSON object must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, object.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The string array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values)
|
||||
.map(value -> "\"" + escape(value) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a field to the object.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param escapedValue The escaped value of the field.
|
||||
*/
|
||||
private void appendFieldUnescaped(String key, String escapedValue) {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("JSON key must not be null");
|
||||
}
|
||||
if (hasAtLeastOneField) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
|
||||
hasAtLeastOneField = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the JSON string and invalidates this builder.
|
||||
*
|
||||
* @return The built JSON string.
|
||||
*/
|
||||
public JsonObject build() {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
JsonObject object = new JsonObject(builder.append("}").toString());
|
||||
builder = null;
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
|
||||
*
|
||||
* <p>This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
|
||||
* Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
|
||||
*
|
||||
* @param value The value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
private static String escape(String value) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
if (c == '"') {
|
||||
builder.append("\\\"");
|
||||
} else if (c == '\\') {
|
||||
builder.append("\\\\");
|
||||
} else if (c <= '\u000F') {
|
||||
builder.append("\\u000").append(Integer.toHexString(c));
|
||||
} else if (c <= '\u001F') {
|
||||
builder.append("\\u00").append(Integer.toHexString(c));
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A super simple representation of a JSON object.
|
||||
*
|
||||
* <p>This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
|
||||
* allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
|
||||
* JsonObject)}.
|
||||
*/
|
||||
public static class JsonObject {
|
||||
|
||||
private final String value;
|
||||
|
||||
private JsonObject(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package me.redtea.nodropx.libs.carcadex.reload;
|
||||
|
||||
public interface Reloadable {
|
||||
void init();
|
||||
|
||||
void close();
|
||||
|
||||
default void reload() {
|
||||
close();
|
||||
init();
|
||||
}
|
||||
|
||||
default int priority() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package me.redtea.nodropx.libs.carcadex.reload.exception;
|
||||
|
||||
public class SimpledReloaderException extends RuntimeException {
|
||||
public SimpledReloaderException() {
|
||||
super("Simpled reloader is not implements this operation. Please use classic reloader");
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package me.redtea.nodropx.libs.carcadex.reload.parameterized;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.reload.Reloadable;
|
||||
import me.redtea.nodropx.libs.carcadex.reload.parameterized.container.ReloadContainer;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class ParameterizedReloadable implements Reloadable {
|
||||
public abstract void init(@Nullable ReloadContainer container);
|
||||
@Override
|
||||
public void init() {
|
||||
throw new NotImplementedException("This class can be initialized only with init(ReloadContainer) method");
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package me.redtea.nodropx.libs.carcadex.reload.parameterized.container;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.reload.parameterized.container.impl.MapReloadContainer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ReloadContainer {
|
||||
<T>T get(String key);
|
||||
<T>void set(String key, T t);
|
||||
boolean contains(String key);
|
||||
static ReloadContainer of(Object... objects) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
for(int i = 0; i < objects.length; i += 2) {
|
||||
String key = (String) objects[i];
|
||||
Object value = objects[i+1];
|
||||
map.put(key, value);
|
||||
}
|
||||
return new MapReloadContainer(map);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package me.redtea.nodropx.libs.carcadex.reload.parameterized.container.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.libs.carcadex.reload.parameterized.container.ReloadContainer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class MapReloadContainer implements ReloadContainer {
|
||||
private final Map<String, Object> params;
|
||||
@Override
|
||||
public <T> T get(String key) {
|
||||
return (T) params.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void set(String key, T t) {
|
||||
params.put(key, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return params.containsKey(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.reload.Reloadable;
|
||||
|
||||
public interface MutableRepo<K, V> extends Repo<K, V>, Reloadable {
|
||||
|
||||
V update(K key, V value);
|
||||
|
||||
V remove(K key);
|
||||
|
||||
void saveAll();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.reload.Reloadable;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.builder.RepoBuilder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface Repo<K, V> extends Reloadable {
|
||||
Collection<V> all();
|
||||
|
||||
Optional<V> get(K key);
|
||||
|
||||
static<K, V> RepoBuilder<K, V> builder() {
|
||||
return RepoBuilder.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.builder;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.repo.MutableRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.builder.impl.RepoBuilderImpl;
|
||||
import me.redtea.nodropx.libs.carcadex.schema.SchemaStrategy;
|
||||
import me.redtea.nodropx.libs.carcadex.serializer.CommonSerializer;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface RepoBuilder<K, V> {
|
||||
RepoBuilder<K, V> serializer(CommonSerializer<V> serializer);
|
||||
RepoBuilder<K, V> binary(); //requires that V extends Serializable
|
||||
RepoBuilder<K, V> plugin(Plugin plugin);
|
||||
RepoBuilder<K, V> autoSave(long period); //in milisec
|
||||
RepoBuilder<K, V> schema(SchemaStrategy<K,V> schemaStrategy);
|
||||
RepoBuilder<K, V> dir(Path dir);
|
||||
RepoBuilder<K, V> dir(String filename); //requires plugin. didnt need if dir set
|
||||
RepoBuilder<K, V> logging();
|
||||
RepoBuilder<K, V> debugLogging();
|
||||
RepoBuilder<K, V> logging(Logger logger);
|
||||
RepoBuilder<K, V> debugLogging(Logger logger);
|
||||
RepoBuilder<K, V> threadSafe();
|
||||
MutableRepo<K, V> build();
|
||||
|
||||
static<K, V> RepoBuilder<K, V> get() {
|
||||
return new RepoBuilderImpl<>();
|
||||
}
|
||||
|
||||
static <K, V> RepoBuilder<K, V> of(Path dir) {
|
||||
return RepoBuilder.<K, V>get().dir(dir);
|
||||
}
|
||||
|
||||
static <K, V> RepoBuilder<K, V> of(Plugin plugin) {
|
||||
return RepoBuilder.<K, V>get().plugin(plugin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.builder.impl;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.MutableRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.builder.RepoBuilder;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.decorator.impl.AutoSaveDecorator;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.decorator.impl.CopyOnWriteDecorator;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.decorator.impl.LoggingDecorator;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.CacheRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.common.CommonRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.schema.SchemaRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.schema.SchemaStrategy;
|
||||
import me.redtea.nodropx.libs.carcadex.serializer.CommonSerializer;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class RepoBuilderImpl<K, V> implements RepoBuilder<K, V> {
|
||||
private CommonSerializer<V> serializer; //null if binary
|
||||
private boolean autoSave;
|
||||
private long period;
|
||||
private Plugin plugin;
|
||||
private Path dir;
|
||||
private String filename;
|
||||
private SchemaStrategy<K, V> schemaStrategy;
|
||||
private Logger logger = null;
|
||||
private boolean logging = false;
|
||||
private boolean debugLogger = false;
|
||||
private boolean threadSafe = false;
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> serializer(CommonSerializer<V> serializer) {
|
||||
this.serializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> binary() {
|
||||
serializer = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> plugin(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> autoSave(long period) {
|
||||
autoSave = true;
|
||||
this.period = period;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> schema(SchemaStrategy<K, V> schemaStrategy) {
|
||||
this.schemaStrategy = schemaStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> dir(Path path) {
|
||||
this.dir = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> dir(String filename) {
|
||||
this.filename = filename;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> logging() {
|
||||
logging = true;
|
||||
debugLogger = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> debugLogging() {
|
||||
logging = true;
|
||||
debugLogger = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> logging(Logger logger) {
|
||||
logging = true;
|
||||
debugLogger = false;
|
||||
this.logger = logger;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> debugLogging(Logger logger) {
|
||||
logging = true;
|
||||
debugLogger = true;
|
||||
this.logger = logger;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoBuilder<K, V> threadSafe() {
|
||||
threadSafe = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableRepo<K, V> build() {
|
||||
if(dir == null) {
|
||||
checkDir();
|
||||
}
|
||||
|
||||
CacheRepo<K, V> result = null;
|
||||
|
||||
if(serializer != null) {
|
||||
result = new CommonRepo<>(dir, serializer);
|
||||
}
|
||||
|
||||
if(schemaStrategy != null) {
|
||||
result = new SchemaRepo<>(schemaStrategy);
|
||||
}
|
||||
|
||||
if(result == null) {
|
||||
result = new CommonRepo<>(dir);
|
||||
}
|
||||
|
||||
if(autoSave) {result = new AutoSaveDecorator<>(result, 0, period);
|
||||
}
|
||||
|
||||
if(logging) {
|
||||
if(logger != null) result = new LoggingDecorator<>(result, logger, debugLogger);
|
||||
else result = new LoggingDecorator<>(result, debugLogger);
|
||||
}
|
||||
|
||||
if(threadSafe) {
|
||||
result = new CopyOnWriteDecorator<>(result);
|
||||
}
|
||||
|
||||
result.init();
|
||||
return result;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void checkDir() {
|
||||
if(plugin == null) {
|
||||
if(filename == null) {
|
||||
dir = Paths.get("saved-data-carcadex");
|
||||
} else dir = Paths.get(filename);
|
||||
} else {
|
||||
dir = new File(plugin.getDataFolder(), filename == null ? "saved-data-carcadex" : filename).toPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.decorator;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.CacheRepo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
public abstract class CacheRepoDecorator<K, V> implements CacheRepo<K, V> {
|
||||
protected final CacheRepo<K, V> repo;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
repo.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
repo.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V update(K key, V value) {
|
||||
return repo.update(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) {
|
||||
return repo.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll() {
|
||||
repo.saveAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> all() {
|
||||
return repo.all();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<V> get(K key) {
|
||||
return repo.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadToCache(K k) {
|
||||
repo.loadToCache(k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFromCache(K k) {
|
||||
repo.remove(k);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.decorator.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.repo.decorator.CacheRepoDecorator;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.CacheRepo;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class AutoSaveDecorator<K, V> extends CacheRepoDecorator<K, V> {
|
||||
public AutoSaveDecorator(CacheRepo<K, V> repo, long delay /*in milisec*/, long period /*in milisec*/) {
|
||||
super(repo);
|
||||
new Timer().scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
saveAll();
|
||||
}
|
||||
}, delay, period);
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.decorator.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.repo.decorator.CacheRepoDecorator;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.CacheRepo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
||||
public class CopyOnWriteDecorator<K, V> extends CacheRepoDecorator<K, V> {
|
||||
private final CacheRepo<K, V> repo;
|
||||
|
||||
public CopyOnWriteDecorator(CacheRepo<K, V> repo) {
|
||||
super(repo);
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
final transient ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
@Override
|
||||
public void loadToCache(K k) {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
super.loadToCache(k);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFromCache(K k) {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
super.removeFromCache(k);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> all() {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
return super.all();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<V> get(K key) {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
return super.get(key);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void saveAll() {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
super.saveAll();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V update(K key, V value) {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
V oldValue = repo.get(key).orElse(null);
|
||||
if (oldValue != null && !oldValue.equals(value)) {
|
||||
repo.update(key, value);
|
||||
}
|
||||
return value;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public V remove(K key) {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
V oldValue = repo.get(key).orElse(null);
|
||||
if (oldValue != null) {
|
||||
repo.remove(key);
|
||||
}
|
||||
return oldValue;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.decorator.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.repo.decorator.CacheRepoDecorator;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.CacheRepo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
public class LoggingDecorator<K, V> extends CacheRepoDecorator<K, V> {
|
||||
private final Logger logger;
|
||||
|
||||
private final boolean onlyOnDebug;
|
||||
|
||||
public LoggingDecorator(CacheRepo<K, V> repo, Logger logger, boolean onlyOnDebug) {
|
||||
super(repo);
|
||||
this.logger = logger;
|
||||
this.onlyOnDebug = onlyOnDebug;
|
||||
}
|
||||
|
||||
public LoggingDecorator(CacheRepo<K, V> repo, boolean onlyOnDebug) {
|
||||
super(repo);
|
||||
this.logger = LoggerFactory.getLogger(this.getClass());
|
||||
this.onlyOnDebug = onlyOnDebug;
|
||||
}
|
||||
|
||||
public LoggingDecorator(CacheRepo<K, V> repo) {
|
||||
super(repo);
|
||||
this.logger = LoggerFactory.getLogger(this.getClass());
|
||||
this.onlyOnDebug = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
log("Start of repository initialization...");
|
||||
super.init();
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on initialization repository! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
log("Closing the repository...");
|
||||
super.close();
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on closing repository! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V update(K key, V value) {
|
||||
try {
|
||||
log("Updating the repository...");
|
||||
log("Key: " + key + " Value: " + value);
|
||||
return repo.update(key, value);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on updating repository! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) {
|
||||
try {
|
||||
log("Removing from repository...");
|
||||
log("Key: " + key);
|
||||
return repo.remove(key);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on removing from repository! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll() {
|
||||
try {
|
||||
log("Saving to storage from repository...");
|
||||
repo.saveAll();
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on saving to storage from repository! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> all() {
|
||||
try {
|
||||
log("Getting all values from memory...");
|
||||
return repo.all();
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on getting all values from memory! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<V> get(K key) {
|
||||
try {
|
||||
log("Getting value from repository...");
|
||||
log("Key: " + key);
|
||||
return repo.get(key);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on getting value from repository! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadToCache(K k) {
|
||||
try {
|
||||
log("Load to cache value...");
|
||||
log("Key: " + k);
|
||||
repo.loadToCache(k);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on load to cache value! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFromCache(K k) {
|
||||
try {
|
||||
log("Remove from cache value...");
|
||||
repo.remove(k);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error on remove from cache value! " + e.getLocalizedMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void log(String log) {
|
||||
if(onlyOnDebug) logger.debug(log);
|
||||
else logger.info(log);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.repo.MutableRepo;
|
||||
|
||||
public interface CacheRepo<K, V> extends MutableRepo<K, V> {
|
||||
void loadToCache(K k);
|
||||
|
||||
void removeFromCache(K k);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.impl.common;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.schema.SchemaRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.schema.impl.binary.BinarySchemaStrategy;
|
||||
import me.redtea.nodropx.libs.carcadex.schema.impl.serialize.SerializeSchemaStrategy;
|
||||
import me.redtea.nodropx.libs.carcadex.serializer.CommonSerializer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class CommonRepo<K, V> extends SchemaRepo<K, V> {
|
||||
public CommonRepo(Path dir, CommonSerializer<V> serializer) {
|
||||
super(new SerializeSchemaStrategy<>(dir, serializer));
|
||||
}
|
||||
|
||||
public CommonRepo(Path dir) {
|
||||
super(new BinarySchemaStrategy<>(dir));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.impl.map;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.reload.parameterized.ParameterizedReloadable;
|
||||
import me.redtea.nodropx.libs.carcadex.reload.parameterized.container.ReloadContainer;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.Repo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Reload container:
|
||||
* data - Map.class
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
*/
|
||||
public abstract class MapRepo<K, V> extends ParameterizedReloadable implements Repo<K, V> {
|
||||
protected Map<K, V> data = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Collection<V> all() {
|
||||
return data.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<V> get(K key) {
|
||||
return Optional.ofNullable(data.get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ReloadContainer container) {
|
||||
if(container != null) data = container.get("data");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
data.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.impl.schema;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.CacheRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.map.MapRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.schema.SchemaStrategy;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SchemaRepo<K, V> extends MapRepo<K, V> implements CacheRepo<K, V> {
|
||||
protected final Set<K> toRemove = new HashSet<>();
|
||||
|
||||
protected final SchemaStrategy<K, V> schemaStrategy;
|
||||
|
||||
public SchemaRepo(SchemaStrategy<K, V> schemaStrategy) {
|
||||
this.schemaStrategy = schemaStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V update(K key, V value) {
|
||||
return data.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> all() {
|
||||
return schemaStrategy.all();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<V> get(K key) {
|
||||
if(toRemove.contains(key)) return Optional.ofNullable(null);
|
||||
V result = data.get(key);
|
||||
if(result == null) try {
|
||||
loadToCache(key);
|
||||
result = data.get(key);
|
||||
} catch (Throwable ignored) {}
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) {
|
||||
if(!get(key).isPresent()) throw new NoSuchElementException();
|
||||
toRemove.add(key);
|
||||
return data.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll() {
|
||||
toRemove.forEach(schemaStrategy::remove);
|
||||
data.forEach(schemaStrategy::insert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadToCache(K key) {
|
||||
data.put(key, schemaStrategy.get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFromCache(K key) {
|
||||
data.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
schemaStrategy.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
saveAll();
|
||||
data.clear();
|
||||
toRemove.clear();
|
||||
schemaStrategy.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
saveAll();
|
||||
data.clear();
|
||||
schemaStrategy.reload();
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.impl.yaml;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.map.MapRepo;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.impl.yaml.strategy.ParseStrategy;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class YamlRepo<K, V> extends MapRepo<K, V> {
|
||||
protected final Path file;
|
||||
|
||||
protected final Plugin plugin;
|
||||
|
||||
protected final ParseStrategy<K, V> parseStrategy;
|
||||
|
||||
public YamlRepo(Path file, Plugin plugin, ParseStrategy<K, V> parseStrategy) {
|
||||
this.file = file;
|
||||
this.plugin = plugin;
|
||||
this.parseStrategy = parseStrategy;
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
protected FileConfiguration initFile() {
|
||||
if (Files.notExists(file)) plugin.saveResource(file.toFile().getName(), false);
|
||||
return YamlConfiguration.loadConfiguration(file.toFile());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
data.putAll(parseStrategy.fromYaml(initFile()));
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package me.redtea.nodropx.libs.carcadex.repo.impl.yaml.strategy;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ParseStrategy<K, V> {
|
||||
Map<K, V> fromYaml(FileConfiguration fileConfiguration);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.redtea.nodropx.libs.carcadex.schema;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public abstract class AbstractFileSchemaStrategy<K, V> implements SchemaStrategy<K, V> {
|
||||
protected final Path dir;
|
||||
|
||||
public AbstractFileSchemaStrategy(Path dir) {
|
||||
this.dir = dir;
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if(Files.notExists(dir)) {
|
||||
try {
|
||||
Files.createDirectory(dir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(K key) {
|
||||
File file = new File(dir.toFile(), String.valueOf(key));
|
||||
if(file.exists()) file.delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package me.redtea.nodropx.libs.carcadex.schema;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.reload.Reloadable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface SchemaStrategy<K, V> extends Reloadable {
|
||||
Collection<V> all();
|
||||
|
||||
V get(K key);
|
||||
|
||||
void insert(K key, V value);
|
||||
|
||||
void remove(K key);
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package me.redtea.nodropx.libs.carcadex.schema.impl.binary;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.schema.AbstractFileSchemaStrategy;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BinarySchemaStrategy<K, V> extends AbstractFileSchemaStrategy<K, V> {
|
||||
|
||||
public BinarySchemaStrategy(Path dir) {
|
||||
super(dir);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<V> all() {
|
||||
if(dir.toFile().listFiles() != null)
|
||||
return Arrays.stream(dir.toFile().listFiles()).map(this::read).collect(Collectors.toList());
|
||||
else return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
File file = new File(dir.toFile(), key+".bin");
|
||||
return read(file);
|
||||
}
|
||||
|
||||
private V read(File file) {
|
||||
try {
|
||||
if(!file.exists()) file.createNewFile();
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
|
||||
V result = (V) objectInputStream.readObject();
|
||||
objectInputStream.close();
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(K key, V value) {
|
||||
try {
|
||||
File file = new File(dir.toFile(), key+".bin");
|
||||
if(!file.exists()) file.createNewFile();
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
|
||||
objectOutputStream.writeObject(value);
|
||||
objectOutputStream.flush();
|
||||
objectOutputStream.close();
|
||||
fileOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package me.redtea.nodropx.libs.carcadex.schema.impl.filesection;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.schema.AbstractFileSchemaStrategy;
|
||||
import me.redtea.nodropx.libs.carcadex.schema.impl.filesection.parser.SectionParser;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class FileSectionSchemaStrategy<K, V> extends AbstractFileSchemaStrategy<K, V> {
|
||||
private final SectionParser sectionParser;
|
||||
|
||||
public FileSectionSchemaStrategy(Path dir, SectionParser sectionParser) {
|
||||
super(dir);
|
||||
this.sectionParser = sectionParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> all() {
|
||||
if(dir.toFile().listFiles() == null) return new ArrayList<>();
|
||||
return Arrays.stream(dir.toFile().listFiles()).map(it -> fromSection(sectionParser.parse(it))).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
return fromSection(sectionParser.parse(Arrays.stream(dir.toFile().listFiles()).filter(it -> it.getName().equals(String.valueOf(key)))
|
||||
.findFirst().get()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(K key, V value) {
|
||||
File file = Arrays.stream(dir.toFile().listFiles()).filter(it -> it.getName().equals(String.valueOf(key)))
|
||||
.findFirst().get();
|
||||
sectionParser.save(saveToSection(value, sectionParser.parse(file)), file);
|
||||
}
|
||||
|
||||
abstract V fromSection(ConfigurationSection section);
|
||||
abstract ConfigurationSection saveToSection(V v, ConfigurationSection section);
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package me.redtea.nodropx.libs.carcadex.schema.impl.filesection.parser;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface SectionParser {
|
||||
ConfigurationSection parse(File file);
|
||||
void save(ConfigurationSection section, File file);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package me.redtea.nodropx.libs.carcadex.schema.impl.filesection.parser.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.schema.impl.filesection.parser.SectionParser;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class YamlSectionParser implements SectionParser {
|
||||
@Override
|
||||
public ConfigurationSection parse(File file) {
|
||||
return YamlConfiguration.loadConfiguration(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(ConfigurationSection section, File file) {
|
||||
try {
|
||||
((FileConfiguration) section).save(file);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package me.redtea.nodropx.libs.carcadex.schema.impl.serialize;
|
||||
|
||||
import me.redtea.nodropx.libs.carcadex.schema.AbstractFileSchemaStrategy;
|
||||
import me.redtea.nodropx.libs.carcadex.serializer.CommonSerializer;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SerializeSchemaStrategy<K, V> extends AbstractFileSchemaStrategy<K, V> {
|
||||
|
||||
private final CommonSerializer<V> serializer;
|
||||
|
||||
public SerializeSchemaStrategy(Path dir, CommonSerializer<V> serializer) {
|
||||
super(dir);
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> all() {
|
||||
return Arrays.stream(dir.toFile().listFiles()).map(file -> {
|
||||
try {
|
||||
return serializer.deserialize(String.join("\n", Files.readAllLines(file.toPath())));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
try {
|
||||
return serializer.deserialize(
|
||||
String.join("\n", Files.readAllLines(
|
||||
Arrays.stream(dir.toFile().listFiles()).filter(it -> it.getName().equals(String.valueOf(key)))
|
||||
.findFirst().get().toPath()
|
||||
)));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(K key, V value) {
|
||||
File file = new File(dir.toFile(), String.valueOf(key));
|
||||
try {
|
||||
if(!file.exists()) file.createNewFile();
|
||||
BufferedWriter writer;
|
||||
writer = new BufferedWriter(new FileWriter(file));
|
||||
writer.write(serializer.serialize(value));
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package me.redtea.nodropx.libs.carcadex.serializer;
|
||||
|
||||
public interface CommonSerializer<T> {
|
||||
String serialize(T t);
|
||||
T deserialize(String s);
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package me.redtea.nodropx.libs.message.container;
|
||||
|
||||
import me.redtea.nodropx.libs.message.container.impl.MessagesImpl;
|
||||
import me.redtea.nodropx.libs.message.factory.MessageFactory;
|
||||
import me.redtea.nodropx.libs.message.factory.impl.LegacyFactoryImpl;
|
||||
import me.redtea.nodropx.libs.message.model.Message;
|
||||
import me.redtea.nodropx.libs.message.model.impl.NullMessage;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* <h1>Container of messages</h1>
|
||||
* <h2>Create container by plugin with file messages.yml</h2>
|
||||
* <h3>Step 1. Create file messages.yml in resources folder (in jar of plugin)</h3>
|
||||
* <p>Write in file your messages in YAML format. Use MiniMessages format for color messages. <br>
|
||||
* See <a href="https://docs.advntr.dev/minimessage/format.html">https://docs.advntr.dev/minimessage/format.html</a></p>
|
||||
* <p>For example I will be use this content:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* error: "<red>Error!"
|
||||
* someRoot:
|
||||
* message1: "<green>Message"
|
||||
* message2:
|
||||
* - "<green>Message start!"
|
||||
* - "<green>Message end!"
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
* <h3>Step 2. Load Messages container in your onEnable method</h3>
|
||||
* <pre>
|
||||
* {@code
|
||||
* Messages messages = Messages.of(this);
|
||||
* }
|
||||
* </pre>
|
||||
* <h3>Step 3. Done! Now you can use the messages variable to manage messages.</h3>
|
||||
* <h2>Create container by plugin with custom file</h2>
|
||||
* <p>Everything is the same, the same, only in jar create a file with the name you need (or several such files)
|
||||
* and change OnEnable like this:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* Messages messages = Messages.of(this, "YOURFILENAME.yml");
|
||||
* }
|
||||
* </pre>
|
||||
* <h2>Create container by ConfigurationSection</h2>
|
||||
* <p>Here are a couple of examples of how this can be done:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* Messages messages = Messages.of(getConfig().getConfigurationSection("messages"));
|
||||
*
|
||||
* //or so
|
||||
*
|
||||
* ConfigurationSection section = ...;
|
||||
* Messages messages = Messages.of(section);
|
||||
* }
|
||||
* </pre>
|
||||
* @since 1.0.0
|
||||
* @author itzRedTea
|
||||
*/
|
||||
public interface Messages {
|
||||
/**
|
||||
* <h1>Get message from container by key/yaml path</h1>
|
||||
*
|
||||
* <h2>For example</h2>
|
||||
* <p><b>Yaml structure:</b></p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* error: "<red>Error!"
|
||||
* someRoot:
|
||||
* message1: "<green>Message"
|
||||
* message2:
|
||||
* - "<green>Message start!"
|
||||
* - "<green>Message end!"
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p><b>Getting messages in code:</b></p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* Messages messages = ...; //Init Messages
|
||||
* CommandSender sender = ...; //Getting CommandSender
|
||||
* messages.get("error").send(sender); //Getting error message
|
||||
* message.get("someRoot.message1").send(sender); //Getting someRoot.message1 message
|
||||
* message.get("someRoot.message2").send(sender); //Getting someRoot.message2 message
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param key key of message
|
||||
* @return Message
|
||||
* @see Message
|
||||
*/
|
||||
Message get(String key);
|
||||
|
||||
/**
|
||||
* <h1>Put message to container</h1>
|
||||
* @param key key/yaml path to message
|
||||
* @param message message to mut
|
||||
* @return putted message
|
||||
* @see Message
|
||||
*/
|
||||
Message put(String key, Message message);
|
||||
|
||||
/**
|
||||
* @param key key/yaml path to message
|
||||
* @return is there a message with such a key in the container?
|
||||
*/
|
||||
boolean has(String key);
|
||||
|
||||
/**
|
||||
* <h1>Reload container with messages in section from args.</h1>
|
||||
* @param section - ConfigurationSection with messages
|
||||
*/
|
||||
void reload(ConfigurationSection section);
|
||||
|
||||
/**
|
||||
* <h1>Sets custom message parse factory.</h1>
|
||||
*/
|
||||
void factory(MessageFactory messageFactory);
|
||||
|
||||
/**
|
||||
* <h1>Create container using configuration section</h1>
|
||||
* Create container using messages from configuration section.
|
||||
* <pre>
|
||||
* {@code
|
||||
* ConfigurationSection section = ...;
|
||||
* Messages messages = Messages.of(section);
|
||||
* }
|
||||
* </pre>
|
||||
* @param section - configuration section with messages
|
||||
* @return container with messages
|
||||
*/
|
||||
static Messages of(ConfigurationSection section) {
|
||||
return new MessagesImpl(section);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1>Create container using current plugin and custom filename</h1>
|
||||
* Create container using file with your name from jar entry by default.
|
||||
* A new file in plugin folder will be created with content from your file.
|
||||
* <pre>
|
||||
* {@code
|
||||
* Messages messages = Messages.of(this, "YOURFILENAME.yml");
|
||||
* }
|
||||
* </pre>
|
||||
* @param plugin - plugin that creates a container
|
||||
* @return container with messages
|
||||
*/
|
||||
static Messages of(Plugin plugin, String fileName) {
|
||||
File file = new File(plugin.getDataFolder(), fileName);
|
||||
if(!file.exists()) plugin.saveResource(fileName, false);
|
||||
return of(YamlConfiguration.loadConfiguration(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1>Create container using current plugin</h1>
|
||||
* Create container using messages.yml file by default from jar entry.
|
||||
* A new file in plugin folder will be created with content from messages.yml
|
||||
* <pre>
|
||||
* {@code
|
||||
* Messages messages = Messages.of(this);
|
||||
* }
|
||||
* </pre>
|
||||
* @param plugin - plugin that creates a container
|
||||
* @return container with messages
|
||||
*/
|
||||
static Messages of(Plugin plugin) {
|
||||
return of(plugin, "text/legacy/messages.yml");
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1>Create container using file</h1>
|
||||
* <pre>
|
||||
* {@code
|
||||
* File file = new File("msg_file.yml");
|
||||
* Messages messages = Messages.of(file);
|
||||
* }
|
||||
* </pre>
|
||||
* @param file - file with messages
|
||||
* @return container with messages
|
||||
*/
|
||||
static Messages of(File file) {
|
||||
return of(YamlConfiguration.loadConfiguration(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1>Create container using path</h1>
|
||||
*
|
||||
* @param path - file with messages
|
||||
* @return container with messages
|
||||
* @see Messages#of(File)
|
||||
*/
|
||||
static Messages of(Path path) {
|
||||
return of(path.toFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Only legacy color codes. For versions < 1.18
|
||||
* @return container with messages
|
||||
*/
|
||||
static Messages legacy(ConfigurationSection section) {
|
||||
return asLegacy(of(section));
|
||||
}
|
||||
|
||||
/** @see Messages#legacy(ConfigurationSection) */
|
||||
static Messages legacy(Plugin plugin, String fileName) {
|
||||
return asLegacy(of(plugin, fileName));
|
||||
}
|
||||
|
||||
/** @see Messages#legacy(ConfigurationSection) */
|
||||
static Messages legacy(Plugin plugin) {
|
||||
return asLegacy(of(plugin));
|
||||
}
|
||||
|
||||
/** @see Messages#legacy(ConfigurationSection) */
|
||||
static Messages legacy(File file) {
|
||||
return asLegacy(of(file));
|
||||
}
|
||||
|
||||
/** @see Messages#legacy(ConfigurationSection) */
|
||||
static Messages asLegacy(Messages messages) {
|
||||
messages.factory(new LegacyFactoryImpl());
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return empty messages container
|
||||
* Use this with Reloader
|
||||
*/
|
||||
static Messages empty() {
|
||||
return new MessagesImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton NULL_MESSAGE
|
||||
* @see NullMessage
|
||||
*/
|
||||
Message NULL_MESSAGE = new NullMessage();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package me.redtea.nodropx.libs.message.container.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.message.container.Messages;
|
||||
import me.redtea.nodropx.libs.message.factory.MessageFactory;
|
||||
import me.redtea.nodropx.libs.message.model.Message;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MessagesImpl implements Messages {
|
||||
private Map<String, Message> messages;
|
||||
|
||||
private MessageFactory factory = MessageFactory.instance;
|
||||
|
||||
private final Message NULL_MESSAGE = factory.nullMessage();
|
||||
|
||||
public MessagesImpl() {
|
||||
}
|
||||
|
||||
public MessagesImpl(@NotNull ConfigurationSection section) {
|
||||
messages = fromConfigurationToMap(section);
|
||||
}
|
||||
|
||||
public MessagesImpl(@NotNull File file) {
|
||||
this(YamlConfiguration.loadConfiguration(file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message get(String key) {
|
||||
return messages.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message put(String key, Message message) {
|
||||
if(!has(key)) return NULL_MESSAGE;
|
||||
return messages.put(key, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String key) {
|
||||
return messages.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(ConfigurationSection section) {
|
||||
messages = fromConfigurationToMap(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void factory(MessageFactory messageFactory) {
|
||||
this.factory = messageFactory;
|
||||
}
|
||||
|
||||
private Map<String, Message> fromConfigurationToMap(@NotNull ConfigurationSection section) {
|
||||
Map<String, Message> data = new HashMap<>();
|
||||
|
||||
section.getKeys(false).forEach(key -> {
|
||||
if (section.isConfigurationSection(key)) {
|
||||
Map<String, Message> newMessages = fromConfigurationToMap(Objects.
|
||||
requireNonNull(section.getConfigurationSection(key)));
|
||||
newMessages.forEach((keyMessage, message) -> data.put(key + "." + keyMessage, message));
|
||||
} else {
|
||||
Message message = null;
|
||||
if (section.isString(key)) {
|
||||
message = factory.message(section.getString(key));
|
||||
} else if (section.isList(key)) {
|
||||
message = factory.message(section.getStringList(key));
|
||||
}
|
||||
|
||||
if (message != null) {
|
||||
data.put(key, message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.redtea.nodropx.libs.message.factory;
|
||||
|
||||
import me.redtea.nodropx.libs.message.factory.impl.MessageFactoryImpl;
|
||||
import me.redtea.nodropx.libs.message.model.Message;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static me.redtea.nodropx.libs.message.container.Messages.NULL_MESSAGE;
|
||||
|
||||
public interface MessageFactory {
|
||||
Message message(List<String> unparsed);
|
||||
Message message(String msg);
|
||||
Message legacyMessage(List<String> unparsed);
|
||||
Message legacyMessage(String msg);
|
||||
|
||||
default Message nullMessage() {
|
||||
return NULL_MESSAGE;
|
||||
}
|
||||
|
||||
MessageFactory instance = new MessageFactoryImpl();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.redtea.nodropx.libs.message.factory.impl;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import me.redtea.nodropx.libs.message.factory.MessageFactory;
|
||||
import me.redtea.nodropx.libs.message.model.Message;
|
||||
import me.redtea.nodropx.libs.message.model.impl.serialized.impl.LegacyMessage;
|
||||
import me.redtea.nodropx.util.MD5ColorUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.md_5.bungee.api.ChatColor.COLOR_CHAR;
|
||||
|
||||
public class LegacyFactoryImpl implements MessageFactory {
|
||||
@Override
|
||||
public Message message(List<String> unparsed) {
|
||||
if(unparsed == null || unparsed.isEmpty()) return nullMessage();
|
||||
if(unparsed.stream().anyMatch(it -> it.contains("&"))) unparsed =
|
||||
unparsed.stream()
|
||||
.map(MD5ColorUtils::translateHexColorCodes)
|
||||
.collect(Collectors.toList());
|
||||
return new LegacyMessage(unparsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message message(String msg) {
|
||||
if(msg == null) return nullMessage();
|
||||
if(msg.contains("&")) msg = MD5ColorUtils.translateHexColorCodes(msg);
|
||||
return new LegacyMessage(Lists.newArrayList(msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message legacyMessage(List<String> unparsed) {
|
||||
return message(unparsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message legacyMessage(String msg) {
|
||||
return message(Lists.newArrayList(msg));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.redtea.nodropx.libs.message.factory.impl;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import me.redtea.nodropx.libs.message.factory.MessageFactory;
|
||||
import me.redtea.nodropx.libs.message.model.Message;
|
||||
import me.redtea.nodropx.libs.message.model.impl.ComponentMessage;
|
||||
import me.redtea.nodropx.libs.message.model.impl.serialized.impl.LegacyMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MessageFactoryImpl implements MessageFactory {
|
||||
@Override
|
||||
public Message message(List<String> unparsed) {
|
||||
if(unparsed == null || unparsed.isEmpty()) return nullMessage();
|
||||
return new ComponentMessage(unparsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message message(String msg) {
|
||||
return message(Lists.newArrayList(msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message legacyMessage(List<String> unparsed) {
|
||||
if(unparsed == null || unparsed.isEmpty()) return nullMessage();
|
||||
return new LegacyMessage(unparsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message legacyMessage(String msg) {
|
||||
return legacyMessage(Lists.newArrayList(msg));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package me.redtea.nodropx.libs.message.model;
|
||||
|
||||
import me.redtea.nodropx.NoDropX;
|
||||
import me.redtea.nodropx.libs.message.model.impl.ComponentMessage;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class AbstractMessage implements Message {
|
||||
protected final List<String> unparsed;
|
||||
|
||||
protected List<Component> parsed = null;
|
||||
|
||||
protected final BukkitAudiences adventure;
|
||||
|
||||
public AbstractMessage(List<String> unparsed) {
|
||||
this.unparsed = unparsed;
|
||||
this.adventure = BukkitAudiences.create(NoDropX.getInstance());
|
||||
}
|
||||
|
||||
protected abstract Component parse(String unparsed);
|
||||
|
||||
private void parse() {
|
||||
parsed = new ArrayList<>();
|
||||
for(String s : unparsed) {
|
||||
parsed.add(parse(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(@NotNull CommandSender sender) {
|
||||
parse();
|
||||
Audience audience = adventure.sender(sender);
|
||||
parsed.forEach(audience::sendMessage);
|
||||
|
||||
}
|
||||
|
||||
private Component cacheComponent = null;
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<Component> asComponentList() {
|
||||
parse();
|
||||
return parsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Message replaceAll(@NotNull String from, @NotNull String to) {
|
||||
return new ComponentMessage(unparsed.stream().map(it -> it.replaceAll(from, to)).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Component asComponent() {
|
||||
if(cacheComponent == null) {
|
||||
parse();
|
||||
Component first = parsed.get(0);
|
||||
for(int i = 1; i < parsed.size(); i++) {
|
||||
first = first.append(parsed.get(i)).append(Component.newline());
|
||||
}
|
||||
cacheComponent = first;
|
||||
}
|
||||
return cacheComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<String> asUnparsedStringList() {
|
||||
return unparsed;
|
||||
}
|
||||
|
||||
private String cacheString = null;
|
||||
@Override
|
||||
@NotNull
|
||||
public String asUnparsedString(@NotNull String newLine) {
|
||||
if(cacheString == null) cacheString = String.join(newLine, unparsed);
|
||||
return cacheString;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.redtea.nodropx.libs.message.model;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <h1>Message</h1>
|
||||
* This class provides message management methods. You can also convert it to Component.
|
||||
* @since 1.0.0
|
||||
* @author itzRedTea
|
||||
*/
|
||||
public interface Message extends ComponentLike {
|
||||
/**
|
||||
* Send message to CommandSender
|
||||
* @param sender recipient of the message
|
||||
*/
|
||||
void send(@NotNull CommandSender sender);
|
||||
|
||||
/**
|
||||
* Replaces all matches in unparsed message
|
||||
* @param from what you want to replace
|
||||
* @param to what do you want to replace
|
||||
* @return copy of the Message with the changes applied to it
|
||||
*/
|
||||
@NotNull
|
||||
Message replaceAll(@NotNull String from, @NotNull String to);
|
||||
|
||||
/**
|
||||
* Convert message to list of components
|
||||
* @return list of components
|
||||
*/
|
||||
@NotNull
|
||||
List<Component> asComponentList();
|
||||
/**
|
||||
* Get unparsed value of message
|
||||
*/
|
||||
@NotNull
|
||||
List<String> asUnparsedStringList();
|
||||
/**
|
||||
* Get unparsed value of message joined to one string
|
||||
*/
|
||||
@NotNull
|
||||
String asUnparsedString(@NotNull String newLine);
|
||||
|
||||
/**
|
||||
* @see Message#asUnparsedString(String) ()
|
||||
*/
|
||||
default String asUnparsedString() {
|
||||
return asUnparsedString("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Message#replaceAll(String, String)
|
||||
*/
|
||||
@NotNull
|
||||
default Message replaceAll(@NotNull Object from, @NotNull Object to) {
|
||||
return replaceAll(String.valueOf(from), String.valueOf(to));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package me.redtea.nodropx.libs.message.model.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.message.model.AbstractMessage;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ComponentMessage extends AbstractMessage {
|
||||
private final MiniMessage mm = MiniMessage.get();
|
||||
|
||||
public ComponentMessage(List<String> unparsed) {
|
||||
super(unparsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component parse(String unparsed) {
|
||||
return mm.deserialize(unparsed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package me.redtea.nodropx.libs.message.model.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.message.model.Message;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class NullMessage implements Message {
|
||||
@Override
|
||||
public void send(@NotNull CommandSender sender) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Message replaceAll(@NotNull String from, @NotNull String to) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<Component> asComponentList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<String> asUnparsedStringList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String asUnparsedString(@NotNull String newLine) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Component asComponent() {
|
||||
return Component.text("");
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package me.redtea.nodropx.libs.message.model.impl.serialized;
|
||||
|
||||
import me.redtea.nodropx.libs.message.model.AbstractMessage;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.serializer.ComponentSerializer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SerializedMessage extends AbstractMessage {
|
||||
private final ComponentSerializer<Component, TextComponent, String> serializer;
|
||||
|
||||
public SerializedMessage(List<String> unparsed, ComponentSerializer<Component, TextComponent, String> serializer) {
|
||||
super(unparsed);
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component parse(String unparsed) {
|
||||
return serializer.deserialize(unparsed);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package me.redtea.nodropx.libs.message.model.impl.serialized.impl;
|
||||
|
||||
import me.redtea.nodropx.libs.message.model.impl.serialized.SerializedMessage;
|
||||
import me.redtea.nodropx.util.MD5ColorUtils;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LegacyMessage extends SerializedMessage {
|
||||
public LegacyMessage(List<String> unparsed) {
|
||||
super(unparsed.stream().map(MD5ColorUtils::translateHexColorCodes).collect(Collectors.toList()),
|
||||
LegacyComponentSerializer.builder().build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package me.redtea.nodropx.listener;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.api.event.NoDropItemThrownEvent;
|
||||
import me.redtea.nodropx.service.event.EventService;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class InventoryHandler implements Listener {
|
||||
private final EventService eventService;
|
||||
|
||||
@EventHandler
|
||||
public void onMove(InventoryClickEvent event) {
|
||||
if(event.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) {
|
||||
call(event, event.getCurrentItem());
|
||||
} else if(event.getAction().equals(InventoryAction.PLACE_ALL)
|
||||
|| event.getAction().equals(InventoryAction.PLACE_ONE)
|
||||
|| event.getAction().equals(InventoryAction.PLACE_SOME)) {
|
||||
call(event, event.getCursor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void call(InventoryClickEvent event, ItemStack item) {
|
||||
eventService.callThrownEvent(
|
||||
(Player)event.getWhoClicked(),
|
||||
item,
|
||||
NoDropItemThrownEvent.Reason.MOVING_TO_OTHER_INVENTORY,
|
||||
Optional.of(event)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package me.redtea.nodropx.listener;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.api.event.NoDropItemThrownEvent;
|
||||
import me.redtea.nodropx.gui.facade.GuiFacade;
|
||||
import me.redtea.nodropx.service.event.EventService;
|
||||
import me.redtea.nodropx.service.respawn.RespawnService;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class PlayerHandler implements Listener {
|
||||
private final RespawnService respawnService;
|
||||
private final EventService eventService;
|
||||
private final GuiFacade guiFacade;
|
||||
|
||||
//TODO check for armor
|
||||
@EventHandler
|
||||
public void onDrop(PlayerDropItemEvent event) {
|
||||
eventService.callThrownEvent(
|
||||
event.getPlayer(),
|
||||
event.getItemDrop().getItemStack(),
|
||||
NoDropItemThrownEvent.Reason.DROP_BY_PLAYER,
|
||||
Optional.of(event)
|
||||
);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onDeath(PlayerDeathEvent event) {
|
||||
respawnService.saveItemsBeforeDeath(event);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onRespawn(PlayerRespawnEvent event) {
|
||||
respawnService.giveSavedItems(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
guiFacade.clearCache(event.getPlayer());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.redtea.nodropx.model.cosmetic;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public interface CosmeticStrategy {
|
||||
void apply(ItemStack itemStack);
|
||||
void loadYaml(Object fromYaml);
|
||||
String tag();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.redtea.nodropx.model.cosmetic.impl;
|
||||
|
||||
import me.redtea.nodropx.model.cosmetic.CosmeticStrategy;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
public class DisplayNameCosmetic implements CosmeticStrategy {
|
||||
private String displayName;
|
||||
|
||||
@Override
|
||||
public void apply(ItemStack itemStack) {
|
||||
ItemMeta meta = itemStack.getItemMeta();
|
||||
meta.setDisplayName(displayName);
|
||||
itemStack.setItemMeta(meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadYaml(Object fromYaml) {
|
||||
displayName = (String) fromYaml;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tag() {
|
||||
return "name";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.redtea.nodropx.model.cosmetic.impl;
|
||||
|
||||
import me.redtea.nodropx.model.cosmetic.CosmeticStrategy;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LoreKyoriCosmetic implements CosmeticStrategy {
|
||||
private List<Component> lore;
|
||||
private final MiniMessage mm = MiniMessage.get();
|
||||
@Override
|
||||
public void apply(ItemStack itemStack) {
|
||||
List<Component> lore = itemStack.lore();
|
||||
if(lore == null) lore = new ArrayList<>();
|
||||
lore.addAll(this.lore);
|
||||
itemStack.lore(lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadYaml(Object fromYaml) {
|
||||
List<String> list = (List<String>) fromYaml;
|
||||
lore = list.stream().map(mm::deserialize).map(it -> Component
|
||||
.empty()
|
||||
.decoration(TextDecoration.ITALIC, false)
|
||||
.append(it)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tag() {
|
||||
return "lore";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.redtea.nodropx.model.cosmetic.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.NoDropX;
|
||||
import me.redtea.nodropx.model.cosmetic.CosmeticStrategy;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoreLegacyCosmetic implements CosmeticStrategy {
|
||||
private List<String> lore;
|
||||
@Override
|
||||
public void apply(ItemStack itemStack) {
|
||||
ItemMeta meta = itemStack.getItemMeta();
|
||||
List<String> lore = meta.getLore();
|
||||
if(lore == null) lore = new ArrayList<>();
|
||||
lore.addAll(this.lore);
|
||||
meta.setLore(lore);
|
||||
itemStack.setItemMeta(meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadYaml(Object fromYaml) {
|
||||
List<String> list = (List<String>) fromYaml;
|
||||
lore = list.stream().map(it -> ChatColor.translateAlternateColorCodes('&', it)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tag() {
|
||||
return "lore";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.redtea.nodropx.model.cosmetic.impl;
|
||||
|
||||
import me.redtea.nodropx.model.cosmetic.CosmeticStrategy;
|
||||
import me.redtea.nodropx.util.MD5ColorUtils;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.md_5.bungee.api.ChatColor.COLOR_CHAR;
|
||||
|
||||
public class LoreMD5Cosmetics implements CosmeticStrategy {
|
||||
|
||||
|
||||
private List<String> lore;
|
||||
@Override
|
||||
public void apply(ItemStack itemStack) {
|
||||
ItemMeta meta = itemStack.getItemMeta();
|
||||
List<String> lore = meta.getLore();
|
||||
if(lore == null) lore = new ArrayList<>();
|
||||
lore.addAll(this.lore);
|
||||
meta.setLore(lore);
|
||||
itemStack.setItemMeta(meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadYaml(Object fromYaml) {
|
||||
List<String> list = (List<String>) fromYaml;
|
||||
lore = list.stream().map(MD5ColorUtils::translateHexColorCodes).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tag() {
|
||||
return "lore";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package me.redtea.nodropx.schema;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.val;
|
||||
import me.redtea.nodropx.libs.carcadex.schema.SchemaStrategy;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ItemStackListSchema implements SchemaStrategy<UUID, List<ItemStack>> {
|
||||
private final File dir;
|
||||
|
||||
@Override
|
||||
public Collection<List<ItemStack>> all() {
|
||||
val res = new ArrayList<List<ItemStack>>();
|
||||
for(File file : dir.listFiles()) {
|
||||
res.add(get(UUID.fromString(file.getName().substring(file.getName().length()-4))));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> get(UUID key) {
|
||||
File file = new File(dir, key.toString() + ".yml");
|
||||
if(!file.exists()) return new ArrayList<>();
|
||||
val conf = YamlConfiguration.loadConfiguration(file);
|
||||
val res = new ArrayList<ItemStack>();
|
||||
for(String k : conf.getKeys(false)) {
|
||||
res.add(conf.getItemStack(k));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void insert(UUID key, List<ItemStack> value) {
|
||||
File file = new File(dir, key.toString() + ".yml");
|
||||
if(!file.exists()) file.createNewFile();
|
||||
val conf = YamlConfiguration.loadConfiguration(file);
|
||||
for(int i = 0; i < value.size(); ++i) {
|
||||
conf.set(String.valueOf(i), value.get(i));
|
||||
}
|
||||
conf.save(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(UUID key) {
|
||||
File file = new File(dir, key.toString() + ".yml");
|
||||
if(file.exists()) file.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void init() {
|
||||
if(!dir.exists()) {
|
||||
Files.createDirectories(dir.toPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package me.redtea.nodropx.service.cosmetic;
|
||||
|
||||
import me.redtea.nodropx.model.cosmetic.CosmeticStrategy;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public interface CosmeticService {
|
||||
void applyAll(ItemStack item);
|
||||
void add(CosmeticStrategy strategy);
|
||||
void load(ConfigurationSection section);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.redtea.nodropx.service.cosmetic.impl;
|
||||
|
||||
import lombok.val;
|
||||
import me.redtea.nodropx.model.cosmetic.CosmeticStrategy;
|
||||
import me.redtea.nodropx.service.cosmetic.CosmeticService;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CosmeticServiceImpl implements CosmeticService {
|
||||
private final List<CosmeticStrategy> strategies = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void applyAll(ItemStack item) {
|
||||
strategies.forEach(it -> {
|
||||
try {
|
||||
it.apply(item);
|
||||
} catch (Throwable e) {
|
||||
Bukkit.getLogger().severe("[NoDropX Cosmetic] Failed to apply cosmetic with tag " + it.tag());
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(CosmeticStrategy strategy) {
|
||||
strategies.add(strategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ConfigurationSection section) {
|
||||
List<CosmeticStrategy> disabled = new ArrayList<>();
|
||||
for(CosmeticStrategy strategy : strategies) {
|
||||
val obj = section.get(strategy.tag());
|
||||
if(obj == null) disabled.add(strategy);
|
||||
else strategy.loadYaml(obj);
|
||||
}
|
||||
strategies.removeAll(disabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.redtea.nodropx.service.event;
|
||||
|
||||
import me.redtea.nodropx.api.event.NoDropItemThrownEvent;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface EventService {
|
||||
void callThrownEvent(Player who, ItemStack item, NoDropItemThrownEvent.Reason reason, Optional<Cancellable> rootEvent);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package me.redtea.nodropx.service.event.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.api.event.NoDropItemThrownEvent;
|
||||
import me.redtea.nodropx.service.event.EventService;
|
||||
import me.redtea.nodropx.service.nodrop.NoDropService;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EventServiceImpl implements EventService {
|
||||
private final NoDropService noDropService;
|
||||
|
||||
@Override
|
||||
public void callThrownEvent(Player who, ItemStack item, NoDropItemThrownEvent.Reason reason, Optional<Cancellable> rootEvent) {
|
||||
if(!noDropService.isNoDrop(item)) return;
|
||||
NoDropItemThrownEvent callingEvent = new NoDropItemThrownEvent(
|
||||
who,
|
||||
reason,
|
||||
item
|
||||
);
|
||||
Bukkit.getPluginManager().callEvent(callingEvent);
|
||||
rootEvent.ifPresent(cancellable -> cancellable.setCancelled(callingEvent.isCancelled()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.redtea.nodropx.service.nbt;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* Service for working with NBT tags
|
||||
*/
|
||||
public interface NBTService {
|
||||
/**
|
||||
* Sets the NBT tag "isNoDrop", which indicates that the item is no drop
|
||||
* @param item editable item
|
||||
* @param isNoDrop tag value
|
||||
* @since 1.0.0
|
||||
* @author redtea
|
||||
*/
|
||||
void setNoDropTag(ItemStack item, boolean isNoDrop);
|
||||
|
||||
/**
|
||||
* Gets the value of the "isNoDrop" tag, which indicates that the item is no drop
|
||||
* @param item checking item
|
||||
* @return tag value
|
||||
* @since 1.0.0
|
||||
* @author redtea
|
||||
*/
|
||||
boolean getNoDropTag(ItemStack item);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.redtea.nodropx.service.nbt.impl;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import me.redtea.nodropx.service.nbt.NBTService;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class Tr7zwNBTServiceImpl implements NBTService {
|
||||
@Override
|
||||
public void setNoDropTag(ItemStack item, boolean isNoDrop) {
|
||||
NBTItem nbtItem = new NBTItem(item);
|
||||
nbtItem.setBoolean("isNoDrop", true);
|
||||
nbtItem.applyNBT(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getNoDropTag(ItemStack item) {
|
||||
if(item == null || item.getType() == Material.AIR) return false;
|
||||
NBTItem nbtItem = new NBTItem(item);
|
||||
return nbtItem.hasTag("isNoDrop") && nbtItem.getBoolean("isNoDrop");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.redtea.nodropx.service.nodrop;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* Service for changing and checking no drop status of item
|
||||
*/
|
||||
public interface NoDropService {
|
||||
/**
|
||||
* Set no drop to item
|
||||
* @param item the item who's no drop property are changing
|
||||
* @param isNoDrop will the item can drop from inventory on death?
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void setNoDrop(ItemStack item, boolean isNoDrop);
|
||||
|
||||
/**
|
||||
* Check if item is no drop item
|
||||
* @param item the item being checked
|
||||
* @return true if item can not drop from inventory on death
|
||||
* @author redtea
|
||||
* @since 1.0.0
|
||||
*/
|
||||
boolean isNoDrop(ItemStack item);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.redtea.nodropx.service.nodrop.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.service.cosmetic.CosmeticService;
|
||||
import me.redtea.nodropx.service.nbt.NBTService;
|
||||
import me.redtea.nodropx.service.nodrop.NoDropService;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class NoDropServiceImpl implements NoDropService {
|
||||
private final NBTService nbtService;
|
||||
private final CosmeticService cosmeticService;
|
||||
|
||||
@Override
|
||||
public void setNoDrop(ItemStack item, boolean isNoDrop) {
|
||||
nbtService.setNoDropTag(item, isNoDrop);
|
||||
cosmeticService.applyAll(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoDrop(ItemStack item) {
|
||||
if(item == null) return false;
|
||||
return nbtService.getNoDropTag(item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.redtea.nodropx.service.respawn;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
|
||||
public interface RespawnService {
|
||||
void giveSavedItems(Player player);
|
||||
void saveItemsBeforeDeath(PlayerDeathEvent event);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package me.redtea.nodropx.service.respawn.impl;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.api.event.NoDropItemDropOnDeathEvent;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.Repo;
|
||||
import me.redtea.nodropx.service.nodrop.NoDropService;
|
||||
import me.redtea.nodropx.service.respawn.RespawnService;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class RespawnServiceImpl implements RespawnService {
|
||||
private final NoDropService noDropService;
|
||||
private final Map<String, Pair[]> toRespawn = new HashMap<>();
|
||||
private final Repo<String, List<Integer>> capacity;
|
||||
|
||||
|
||||
@Override
|
||||
public void giveSavedItems(Player player) {
|
||||
if(toRespawn.containsKey(player.getName())) {
|
||||
Pair[] pairs = toRespawn.get(player.getName());
|
||||
for(Pair pair : pairs) player.getInventory().setItem(pair.slot, pair.item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveItemsBeforeDeath(PlayerDeathEvent event) {
|
||||
Player player = event.getEntity();
|
||||
Set<Integer> noDropSlots = new HashSet<>();
|
||||
for(int i = 0; i < player.getInventory().getSize(); ++i) {
|
||||
ItemStack item = player.getInventory().getItem(i);
|
||||
if(!noDropService.isNoDrop(item)) continue;
|
||||
NoDropItemDropOnDeathEvent callingEvent = new NoDropItemDropOnDeathEvent(
|
||||
player,
|
||||
item
|
||||
);
|
||||
Bukkit.getPluginManager().callEvent(callingEvent);
|
||||
|
||||
if(!callingEvent.isWillDropped()) {
|
||||
noDropSlots.add(i);
|
||||
noDropSlots.addAll(getProtectedSlots(item));
|
||||
}
|
||||
}
|
||||
|
||||
List<Pair> toSave = new ArrayList<>();
|
||||
for(int slot : noDropSlots) {
|
||||
ItemStack item = player.getInventory().getItem(slot);
|
||||
toSave.add(new Pair(slot, item));
|
||||
event.getDrops().remove(item);
|
||||
}
|
||||
|
||||
if(toSave.isEmpty()) return;
|
||||
toRespawn.put(player.getName(), toSave.toArray(new Pair[0]));
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
static class Pair {
|
||||
int slot;
|
||||
ItemStack item;
|
||||
}
|
||||
|
||||
private List<Integer> getProtectedSlots(ItemStack i) {
|
||||
return capacity.get(i.getType().name()).orElse(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.redtea.nodropx.service.storage;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface StorageService {
|
||||
void addItem(UUID playerId, ItemStack item);
|
||||
void removeItem(UUID playerId, ItemStack item);
|
||||
List<ItemStack> getItems(UUID playerId);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.redtea.nodropx.service.storage.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.redtea.nodropx.gui.facade.GuiFacade;
|
||||
import me.redtea.nodropx.libs.carcadex.repo.MutableRepo;
|
||||
import me.redtea.nodropx.service.nodrop.NoDropService;
|
||||
import me.redtea.nodropx.service.storage.StorageService;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class StorageServiceImpl implements StorageService {
|
||||
private final GuiFacade guiFacade;
|
||||
private final MutableRepo<UUID, List<ItemStack>> pagesRepo;
|
||||
private final NoDropService noDropService;
|
||||
|
||||
@Override
|
||||
public void addItem(UUID playerId, ItemStack item) {
|
||||
List<ItemStack> items = pagesRepo.get(playerId).orElse(new ArrayList<>());
|
||||
if(!noDropService.isNoDrop(item)) throw new IllegalArgumentException("Item must be no drop!");
|
||||
items.add(item);
|
||||
pagesRepo.update(playerId, items);
|
||||
guiFacade.clearCache(Bukkit.getPlayer(playerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItem(UUID playerId, ItemStack item) {
|
||||
List<ItemStack> items = pagesRepo.get(playerId).orElse(null);
|
||||
if(items == null) return;
|
||||
items.remove(item);
|
||||
pagesRepo.update(playerId, items);
|
||||
guiFacade.clearCache(Bukkit.getPlayer(playerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getItems(UUID playerId) {
|
||||
return pagesRepo.get(playerId).orElse(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.redtea.nodropx.util;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.md_5.bungee.api.ChatColor.COLOR_CHAR;
|
||||
|
||||
public class MD5ColorUtils {
|
||||
private static final Pattern HEX_PATTERN = Pattern.compile("&#([a-f0-9]{6})");
|
||||
|
||||
public static String translateHexColorCodes(String message) {
|
||||
Matcher matcher = HEX_PATTERN.matcher(message);
|
||||
StringBuffer buffer = new StringBuffer(message.length() + 4 * 8);
|
||||
while (matcher.find()) {
|
||||
String group = matcher.group(1);
|
||||
matcher.appendReplacement(buffer, COLOR_CHAR + "x"
|
||||
+ COLOR_CHAR + group.charAt(0) + COLOR_CHAR + group.charAt(1)
|
||||
+ COLOR_CHAR + group.charAt(2) + COLOR_CHAR + group.charAt(3)
|
||||
+ COLOR_CHAR + group.charAt(4) + COLOR_CHAR + group.charAt(5)
|
||||
);
|
||||
}
|
||||
return ChatColor.translateAlternateColorCodes('&', matcher.appendTail(buffer).toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package me.redtea.nodropx.util;
|
||||
|
||||
import lombok.val;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MaterialUtils {
|
||||
@NotNull
|
||||
public static Material getMaterialFromString(String s) {
|
||||
if(s == null || s.equals("")) return Material.AIR;
|
||||
Material material = Material.matchMaterial(s);
|
||||
if(material != null) return material;
|
||||
|
||||
String[] spl = s.split(":");
|
||||
|
||||
if (spl[0].equals("minecraft")) {
|
||||
material = Material.matchMaterial(spl[1]);
|
||||
} else {
|
||||
if(spl.length < 2) {
|
||||
material = Material.matchMaterial(spl[0]);
|
||||
} else {
|
||||
material = Material.matchMaterial(spl[0]+"_"+spl[1]);;
|
||||
}
|
||||
}
|
||||
|
||||
return material == null ? Material.AIR : material;
|
||||
}
|
||||
|
||||
public void remove(Inventory inventory, Material type, int amount) {
|
||||
if (amount <= 0) return;
|
||||
int size = inventory.getSize();
|
||||
for (int slot = 0; slot < size; slot++) {
|
||||
ItemStack is = inventory.getItem(slot);
|
||||
if (is == null) continue;
|
||||
if (type == is.getType()) {
|
||||
int newAmount = is.getAmount() - amount;
|
||||
if (newAmount > 0) {
|
||||
is.setAmount(newAmount);
|
||||
break;
|
||||
} else {
|
||||
inventory.clear(slot);
|
||||
amount = -newAmount;
|
||||
if (amount == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
CHAINMAIL_HELMET: [ 9, 10, 11, 12, 13, 14, 15, 16, 17 ]
|
||||
@@ -0,0 +1,2 @@
|
||||
forceUseLegacy: false
|
||||
forceUseMD5Colors: false
|
||||
@@ -0,0 +1,16 @@
|
||||
name: NoDropX
|
||||
version: '${version}'
|
||||
main: me.redtea.nodropx.NoDropX
|
||||
api-version: '1.13'
|
||||
description: Set no drop status to your items
|
||||
author: itzRedTea
|
||||
authors: [itzRedTea]
|
||||
|
||||
permissions:
|
||||
nodropx.give:
|
||||
description: "Allows give no drop items"
|
||||
nodropx.reload:
|
||||
description: "Allows reload the plugin"
|
||||
nodropx.gui:
|
||||
escription: "Allows open no drop gui"
|
||||
default: true
|
||||
@@ -0,0 +1,3 @@
|
||||
#name: "No drop"
|
||||
lore:
|
||||
- "&7[&a✔&7] &aNo drop item!"
|
||||
@@ -0,0 +1,23 @@
|
||||
usage:
|
||||
- "&7[ &aUsage of command &e/nodrop &7]"
|
||||
- "&7| &a/give [player] [item] (amount) &e- give no drop item"
|
||||
give:
|
||||
fail: "&4[!] &cFail! Check item type for valid."
|
||||
success: "&2&l[!] &aGived &e%item%&a to &e%player%&a amount &e%amount%&a!"
|
||||
reload: "&aPlugin successfully reloaded at %time% ms"
|
||||
|
||||
noPerms: "&4&l[!] &cNo perms for this!"
|
||||
noConsole: "&4&l[!] &cOnly players can use it!"
|
||||
noExists: "&4&l[!] &cThis player doesnt exists!"
|
||||
|
||||
gui:
|
||||
title: "&lPersonal storage"
|
||||
frame:
|
||||
name: "&7"
|
||||
material: GLASS_PANE
|
||||
prev:
|
||||
name: "&cPrevious"
|
||||
material: ARROW
|
||||
next:
|
||||
name: "&cNext"
|
||||
material: ARROW
|
||||
@@ -0,0 +1,3 @@
|
||||
#name: "No drop"
|
||||
lore:
|
||||
- "&7[&a✔&7] Dcf0cNcd41fo fd932dIdd46rMe259obe76cp eec7fiRf192tVf5a6eYfab9mdffcc!!"
|
||||
@@ -0,0 +1,23 @@
|
||||
usage:
|
||||
- "&7[ &aUsage of command &e/nodrop &7]"
|
||||
- "&7| &a/give [player] [item] (amount) &e- give no drop item"
|
||||
give:
|
||||
fail: "&4[!] &cFail! Check item type for valid."
|
||||
success: "&2&l[!] &aGived &e%item%&a to &e%player%&a amount &e%amount%&a!"
|
||||
reload: "&aPlugin successfully reloaded at %time% ms"
|
||||
|
||||
noPerms: "&4&l[!] &cNo perms for this!"
|
||||
noConsole: "&4&l[!] &cOnly players can use it!"
|
||||
noExists: "&4&l[!] &cThis player doesnt exists!"
|
||||
|
||||
gui:
|
||||
title: "&lPersonal storage"
|
||||
frame:
|
||||
name: "&7"
|
||||
material: GLASS_PANE
|
||||
prev:
|
||||
name: "&cPrevious"
|
||||
material: ARROW
|
||||
next:
|
||||
name: "&cNext"
|
||||
material: ARROW
|
||||
@@ -0,0 +1,3 @@
|
||||
#name: "No drop"
|
||||
lore:
|
||||
- "<gray>[<green>✔<gray>] <gradient:#68CF0C:#8DFFCC>No drop item!"
|
||||
@@ -0,0 +1,24 @@
|
||||
usage:
|
||||
- "<gray>[ <green>Usage of command <yellow>/nodrop <gray>]"
|
||||
- "<gray>| <green>/give [player] [item] (amount) <yellow>- give no drop item"
|
||||
give:
|
||||
fail: "<dark_red><bold>[!] <red>Fail! Check item type for valid."
|
||||
success: "<green><bold>[!] <reset><green>Gived <yellow>%item%<green> to <yellow>%player%<green> amount <yellow>%amount%<green>!"
|
||||
reload: "<green>Plugin successfully reloaded at %time% ms"
|
||||
|
||||
|
||||
noPerms: "<dark_red><bold>[!] <red>No perms for this!"
|
||||
noConsole: "<dark_red><bold>[!] <red>Only players can use it!"
|
||||
noExists: "<dark_red><bold>[!] <red>This player doesnt exists!"
|
||||
|
||||
gui:
|
||||
title: "<bold>Personal storage"
|
||||
frame:
|
||||
name: "&7"
|
||||
material: GLASS_PANE
|
||||
prev:
|
||||
name: "<red>Previous"
|
||||
material: ARROW
|
||||
next:
|
||||
name: "<red>Next"
|
||||
material: ARROW
|
||||
Reference in new issue
Block a user