Compare commits

...

26 Commits

Author SHA1 Message Date
hyperdefined
11d6a5bf91 add new stuff to readme 2026-04-06 14:27:57 -04:00
hyperdefined
df4fadb1f0 remove dupe comment 2026-04-06 12:20:09 -04:00
hyperdefined
c5deb0f7fd config updater 17 2026-04-06 12:19:11 -04:00
hyperdefined
27427c9ee6 RS multikill support
still experimental
2026-04-05 21:07:54 -04:00
hyperdefined
7cd5e8f0d0 make sure we check for canceled events 2026-04-05 20:22:30 -04:00
hyperdefined
6a13c7fef7 add logs stripped 2026-04-05 20:18:43 -04:00
hyperdefined
a785e331b3 git change 2026-04-05 01:30:33 -04:00
hyperdefined
16462d8d81 change api version 2026-04-05 01:07:15 -04:00
hyperdefined
d06cad1c5f Merge pull request #122 from hyperdefined/dependabot/gradle/gradle-wrapper-9.4.1
Bump gradle-wrapper from 9.4.0 to 9.4.1
2026-04-05 00:56:32 -04:00
hyperdefined
7360e4ac2f finally 2026-04-05 00:55:11 -04:00
hyperdefined
4513804c40 random things 2026-04-04 23:54:52 -04:00
hyperdefined
1d56d77bdb add wrapper 2026-04-04 23:51:23 -04:00
hyperdefined
906b13f91e maybe work now... 2026-04-04 23:50:41 -04:00
hyperdefined
c801ce18ec try this 2026-04-04 23:48:12 -04:00
dependabot[bot]
75692f0a05 Bump gradle-wrapper from 9.4.0 to 9.4.1
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.4.0 to 9.4.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.4.0...v9.4.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-05 03:47:31 +00:00
hyperdefined
018b7fee9b update dependabot 2026-04-04 23:46:06 -04:00
hyperdefined
3a2b18907e change workflow 2026-04-04 23:45:31 -04:00
hyperdefined
6217015862 convert to gradle 2026-04-04 23:44:23 -04:00
hyperdefined
46db1af915 add missing creating mode checks here 2026-04-03 19:04:40 -04:00
hyperdefined
772acf4332 mc 26 update 2026-04-03 19:01:24 -04:00
hyperdefined
1cd38294cc bump 2026-04-03 18:52:38 -04:00
hyperdefined
9e01726b84 experimental RS support
does not support multikill
2026-04-03 18:52:16 -04:00
hyperdefined
1176b57886 update java 2026-04-02 16:24:48 -04:00
hyperdefined
5c54182301 check for baby sheep here 2026-04-02 16:23:57 -04:00
hyperdefined
47846df0e4 Merge pull request #121 from hyperdefined/dependabot/maven/com.github.hyperdefined-hyperlib-1.0.10
Bump com.github.hyperdefined:hyperlib from 1.0.9 to 1.0.10
2026-04-02 07:25:17 -04:00
dependabot[bot]
b666323c4a Bump com.github.hyperdefined:hyperlib from 1.0.9 to 1.0.10
Bumps [com.github.hyperdefined:hyperlib](https://github.com/hyperdefined/hyperlib) from 1.0.9 to 1.0.10.
- [Release notes](https://github.com/hyperdefined/hyperlib/releases)
- [Commits](https://github.com/hyperdefined/hyperlib/compare/1.0.9...1.0.10)

---
updated-dependencies:
- dependency-name: com.github.hyperdefined:hyperlib
  dependency-version: 1.0.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-02 11:05:22 +00:00
64 changed files with 1051 additions and 225 deletions

0
.deepsource.toml Normal file → Executable file
View File

0
.github/FUNDING.yml vendored Normal file → Executable file
View File

2
.github/dependabot.yml vendored Normal file → Executable file
View File

@@ -1,6 +1,6 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: maven - package-ecosystem: gradle
directory: "/" directory: "/"
schedule: schedule:
interval: daily interval: daily

24
.github/workflows/build.yml vendored Executable file
View File

@@ -0,0 +1,24 @@
name: Build with Gradle
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 25
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 25
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Build with Gradle
run: ./gradlew build

0
.github/workflows/hangar.yml vendored Normal file → Executable file
View File

View File

@@ -1,25 +0,0 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Build with Maven
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Build with Maven
run: mvn -B package --file pom.xml

0
.github/workflows/modrinth.yml vendored Normal file → Executable file
View File

17
.gitignore vendored Normal file → Executable file
View File

@@ -18,7 +18,6 @@ out/
*.ctxt *.ctxt
# Package Files # # Package Files #
*.jar
*.war *.war
*.nar *.nar
*.ear *.ear
@@ -95,19 +94,9 @@ $RECYCLE.BIN/
# Windows shortcuts # Windows shortcuts
*.lnk *.lnk
target/ # Gradle
.gradle/
pom.xml.tag build/
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
# Common working directory # Common working directory
run/ run/

0
LICENSE Normal file → Executable file
View File

4
README.md Normal file → Executable file
View File

@@ -25,9 +25,13 @@ Here is everything it tracks:
* Flight time with elytras. * Flight time with elytras.
* Critical strikes for melee weapons. * Critical strikes for melee weapons.
* Times trident thrown. * Times trident thrown.
* Logs stripped.
The best part is, this data is stored on the item itself. The best part is, this data is stored on the item itself.
This plugin also has compatibility for:
* [RoseStacker](https://modrinth.com/plugin/rosestacker)
If item lore is ever incorrect/missing, you can run `/toolstats reset`. This command fixes the lore on whatever item you are holding. If item lore is ever incorrect/missing, you can run `/toolstats reset`. This command fixes the lore on whatever item you are holding.
![Image](https://docs.hyper.lol/plugins/toolstats/assets/image.png) ![Image](https://docs.hyper.lol/plugins/toolstats/assets/image.png)

46
build.gradle.kts Normal file
View File

@@ -0,0 +1,46 @@
/*
* This file was generated by the Gradle 'init' task.
*/
plugins {
`java-library`
}
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.rosewooddev.io/repository/public/")
mavenCentral()
maven("https://jitpack.io")
}
dependencies {
compileOnly("io.papermc.paper:paper-api:26.1.1.build.+")
compileOnly("dev.rosewood:rosestacker:1.5.39")
compileOnly("com.github.hyperdefined:hyperlib:1.0.14:all")
}
group = "lol.hyper"
version = "2.0.3"
description = "ToolStats"
java.sourceCompatibility = JavaVersion.VERSION_25
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
tasks.withType<Javadoc> {
options.encoding = "UTF-8"
}
val pluginVersion = version.toString()
tasks.processResources {
filteringCharset = "UTF-8"
val props = mapOf("version" to pluginVersion)
inputs.properties(props)
filesMatching("paper-plugin.yml") {
expand(props)
}
}

5
gradle.properties Normal file
View File

@@ -0,0 +1,5 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
org.gradle.configuration-cache=true

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

Binary file not shown.

View File

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

248
gradlew vendored Executable file
View File

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

93
gradlew.bat vendored Normal file
View File

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

94
pom.xml
View File

@@ -1,94 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of ToolStats.
~
~ ToolStats is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ ToolStats is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>lol.hyper</groupId>
<artifactId>toolstats</artifactId>
<version>2.0.2</version>
<packaging>jar</packaging>
<name>ToolStats</name>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>auto-clean</id>
<phase>initialize</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.hyperdefined</groupId>
<artifactId>hyperlib</artifactId>
<version>1.0.9</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

5
settings.gradle.kts Normal file
View File

@@ -0,0 +1,5 @@
/*
* This file was generated by the Gradle 'init' task.
*/
rootProject.name = "toolstats"

View File

@@ -23,6 +23,7 @@ import lol.hyper.hyperlib.releases.HyperUpdater;
import lol.hyper.hyperlib.utils.TextUtils; import lol.hyper.hyperlib.utils.TextUtils;
import lol.hyper.toolstats.commands.CommandToolStats; import lol.hyper.toolstats.commands.CommandToolStats;
import lol.hyper.toolstats.events.*; import lol.hyper.toolstats.events.*;
import lol.hyper.toolstats.support.rosestacker.RoseStacker;
import lol.hyper.toolstats.tools.*; import lol.hyper.toolstats.tools.*;
import lol.hyper.toolstats.tools.config.ConfigTools; import lol.hyper.toolstats.tools.config.ConfigTools;
import lol.hyper.toolstats.tools.config.ConfigUpdater; import lol.hyper.toolstats.tools.config.ConfigUpdater;
@@ -36,7 +37,7 @@ import java.io.File;
public final class ToolStats extends JavaPlugin { public final class ToolStats extends JavaPlugin {
public final int CONFIG_VERSION = 16; public final int CONFIG_VERSION = 17;
public final ComponentLogger logger = this.getComponentLogger(); public final ComponentLogger logger = this.getComponentLogger();
public final File configFile = new File(this.getDataFolder(), "config.yml"); public final File configFile = new File(this.getDataFolder(), "config.yml");
public boolean tokens = false; public boolean tokens = false;
@@ -74,6 +75,7 @@ public final class ToolStats extends JavaPlugin {
public ToolStatsKeys toolStatsKeys; public ToolStatsKeys toolStatsKeys;
public InventoryClose inventoryClose; public InventoryClose inventoryClose;
public PlayerDrop playerDrop; public PlayerDrop playerDrop;
public RoseStacker roseStacker = null;
@Override @Override
public void onEnable() { public void onEnable() {
@@ -128,6 +130,10 @@ public final class ToolStats extends JavaPlugin {
projectileShoot = new ProjectileShoot(this); projectileShoot = new ProjectileShoot(this);
inventoryClose = new InventoryClose(this); inventoryClose = new InventoryClose(this);
playerDrop = new PlayerDrop(this); playerDrop = new PlayerDrop(this);
if (Bukkit.getPluginManager().isPluginEnabled("RoseStacker")) {
logger.info("RoseStacker has been detected, adding support!");
roseStacker = new RoseStacker(this);
}
Bukkit.getServer().getPluginManager().registerEvents(blockBreak, this); Bukkit.getServer().getPluginManager().registerEvents(blockBreak, this);
Bukkit.getServer().getPluginManager().registerEvents(chunkPopulate, this); Bukkit.getServer().getPluginManager().registerEvents(chunkPopulate, this);

View File

@@ -30,7 +30,7 @@ public class ToolStatsLoader implements PluginLoader {
MavenLibraryResolver resolver = new MavenLibraryResolver(); MavenLibraryResolver resolver = new MavenLibraryResolver();
resolver.addRepository(new RemoteRepository.Builder("jitpack", "default", "https://jitpack.io").build()); resolver.addRepository(new RemoteRepository.Builder("jitpack", "default", "https://jitpack.io").build());
resolver.addDependency(new Dependency(new DefaultArtifact("com.github.hyperdefined:hyperlib:1.0.9"), null)); resolver.addDependency(new Dependency(new DefaultArtifact("com.github.hyperdefined:hyperlib:jar:all:1.0.14"), null));
classpathBuilder.addLibrary(resolver); classpathBuilder.addLibrary(resolver);
} }

View File

@@ -231,7 +231,6 @@ public class CommandToolStats implements BasicCommand {
sender.sendMessage(Component.text("Invalid sub-command.", NamedTextColor.RED)); sender.sendMessage(Component.text("Invalid sub-command.", NamedTextColor.RED));
} }
} }
return;
} }
@Override @Override
@@ -424,6 +423,14 @@ public class CommandToolStats implements BasicCommand {
} }
} }
} }
if (toolStats.config.getBoolean("enabled.logs-stripped")) {
if (container.has(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER)) {
Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (logsStripped != null) {
lore.add(toolStats.configTools.formatLore("logs-stripped", "{logs}", toolStats.numberFormat.formatInt(logsStripped)));
}
}
}
finalMeta.lore(lore); finalMeta.lore(lore);
finalItem.setItemMeta(finalMeta); finalItem.setItemMeta(finalMeta);
int slot = player.getInventory().getHeldItemSlot(); int slot = player.getInventory().getHeldItemSlot();
@@ -879,6 +886,36 @@ public class CommandToolStats implements BasicCommand {
} }
break; break;
} }
case "logs-stripped": {
if (!toolStats.config.getBoolean("enabled.logs-stripped")) {
player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED));
return;
}
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
int value;
try {
value = Integer.parseInt((String) userValue);
} catch (NumberFormatException exception) {
player.sendMessage(Component.text("That is not a valid number.", NamedTextColor.RED));
return;
}
if (value < 0) {
player.sendMessage(Component.text("Number must be positive.", NamedTextColor.RED));
return;
}
Integer statValue = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (statValue == null) {
player.sendMessage(Component.text("Unable to get stat from item.", NamedTextColor.RED));
return;
}
int difference = value - statValue;
editedItemMeta = toolStats.itemLore.updateLogsStripped(editedItem, difference);
updated = true;
} else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
}
break;
}
default: { default: {
player.sendMessage(Component.text("That is not a valid stat to update.", NamedTextColor.RED)); player.sendMessage(Component.text("That is not a valid stat to update.", NamedTextColor.RED));
return; return;
@@ -1272,6 +1309,34 @@ public class CommandToolStats implements BasicCommand {
} }
break; break;
} }
case "logs-stripped": {
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
Integer statValue = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (statValue == null) {
player.sendMessage(Component.text("Unable to get stat from item.", NamedTextColor.RED));
return;
}
String tokens = container.get(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING);
if (tokens == null) {
player.sendMessage(Component.text("Unable to get tokens from item.", NamedTextColor.RED));
return;
}
container.remove(toolStats.toolStatsKeys.getLogsStripped());
List<String> newTokens = toolStats.itemChecker.removeToken(tokens, "logs-stripped");
if (newTokens.isEmpty()) {
container.remove(toolStats.toolStatsKeys.getTokenApplied());
} else {
container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, String.join(",", newTokens));
}
Component oldLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", toolStats.numberFormat.formatInt(statValue));
List<Component> newLore = toolStats.itemLore.removeLore(editedItemMeta.lore(), oldLine);
editedItemMeta.lore(newLore);
} else {
player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED));
}
break;
}
default: { default: {
player.sendMessage(Component.text("That is not a valid stat to update.", NamedTextColor.RED)); player.sendMessage(Component.text("That is not a valid stat to update.", NamedTextColor.RED));
return; return;

View File

@@ -39,7 +39,7 @@ public class AnvilEvent implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onAnvilEvent(PrepareAnvilEvent event) { public void onAnvilEvent(PrepareAnvilEvent event) {
// only listen if the token system is enabled // only listen if the token system is enabled
if (!toolStats.config.getBoolean("tokens.enabled")) { if (!toolStats.config.getBoolean("tokens.enabled")) {
@@ -139,6 +139,10 @@ public class AnvilEvent implements Listener {
addToken(event, tokenType, "critical-strikes", clone); addToken(event, tokenType, "critical-strikes", clone);
return; return;
} }
if (tokenType.equalsIgnoreCase("logs-stripped")) {
addToken(event, tokenType, "logs-stripped", clone);
return;
}
} }
return; return;
} }
@@ -363,6 +367,15 @@ public class AnvilEvent implements Listener {
} }
break; break;
} }
case "logs-stripped": {
if (toolStats.config.getBoolean("enabled.logs-stripped")) {
newItem.setItemMeta(toolStats.itemLore.updateLogsStripped(newItem, 0));
} else {
event.setResult(null);
return;
}
break;
}
} }
event.setResult(newItem); event.setResult(newItem);
event.getView().setRepairCost(toolStats.itemChecker.getCost(targetToken)); event.getView().setRepairCost(toolStats.itemChecker.getCost(targetToken));
@@ -496,6 +509,14 @@ public class AnvilEvent implements Listener {
meta = toolStats.itemLore.updateTridentThrows(finalItem, -tridentThrows); meta = toolStats.itemLore.updateTridentThrows(finalItem, -tridentThrows);
finalItem.setItemMeta(meta); finalItem.setItemMeta(meta);
} }
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (logsStripped == null) {
return;
}
meta = toolStats.itemLore.updateLogsStripped(finalItem, -logsStripped);
finalItem.setItemMeta(meta);
}
event.setResult(finalItem); event.setResult(finalItem);
event.getView().setRepairCost(toolStats.itemChecker.getCost("reset")); event.getView().setRepairCost(toolStats.itemChecker.getCost("reset"));
} }

View File

@@ -39,17 +39,14 @@ import java.util.Locale;
public class BlockBreak implements Listener { public class BlockBreak implements Listener {
private final ToolStats toolStats; private final ToolStats toolStats;
public List<Block> brokenContainers = new ArrayList<>(); public final List<Block> brokenContainers = new ArrayList<>();
public BlockBreak(ToolStats toolStats) { public BlockBreak(ToolStats toolStats) {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBreak(BlockBreakEvent event) { public void onBreak(BlockBreakEvent event) {
if (event.isCancelled()) {
return;
}
Player player = event.getPlayer(); Player player = event.getPlayer();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {
return; return;

View File

@@ -45,7 +45,7 @@ public class BlockDispenseEvent implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onDispense(BlockDispenseLootEvent event) { public void onDispense(BlockDispenseLootEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (player == null) { if (player == null) {

View File

@@ -44,7 +44,7 @@ public class ChunkPopulate implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPopulate(ChunkPopulateEvent event) { public void onPopulate(ChunkPopulateEvent event) {
if (event.getChunk().getWorld().getEnvironment() != World.Environment.THE_END) { if (event.getChunk().getWorld().getEnvironment() != World.Environment.THE_END) {
return; return;
@@ -56,7 +56,7 @@ public class ChunkPopulate implements Listener {
// this is delayed because entities are not loaded instantly // this is delayed because entities are not loaded instantly
// we just check 1 second later // we just check 1 second later
Chunk chunk = event.getChunk(); Chunk chunk = event.getChunk();
Bukkit.getRegionScheduler().runDelayed(toolStats, world, chunk.getX(), chunk.getZ(), scheduledTask -> { Bukkit.getRegionScheduler().runDelayed(toolStats, world, chunk.getX(), chunk.getZ(), _ -> {
for (Entity entity : chunk.getEntities()) { for (Entity entity : chunk.getEntities()) {
// if there is a new item frame // if there is a new item frame
if (!(entity instanceof ItemFrame itemFrame)) { if (!(entity instanceof ItemFrame itemFrame)) {

View File

@@ -44,11 +44,8 @@ public class CraftItem implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onCraft(CraftItemEvent event) { public void onCraft(CraftItemEvent event) {
if (event.isCancelled()) {
return;
}
Player player = (Player) event.getWhoClicked(); Player player = (Player) event.getWhoClicked();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {
return; return;

View File

@@ -23,6 +23,7 @@ import net.kyori.adventure.text.Component;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCreativeEvent; import org.bukkit.event.inventory.InventoryCreativeEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -42,7 +43,7 @@ public class CreativeEvent implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onCreativeEvent(InventoryCreativeEvent event) { public void onCreativeEvent(InventoryCreativeEvent event) {
Player player = (Player) event.getWhoClicked(); Player player = (Player) event.getWhoClicked();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {

View File

@@ -46,12 +46,8 @@ public class EntityDamage implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onDamage(EntityDamageByEntityEvent event) { public void onDamage(EntityDamageByEntityEvent event) {
if (event.isCancelled()) {
return;
}
if (!(event.getEntity() instanceof LivingEntity mobBeingAttacked)) { if (!(event.getEntity() instanceof LivingEntity mobBeingAttacked)) {
return; return;
} }
@@ -76,6 +72,9 @@ public class EntityDamage implements Listener {
if (playerBeingAttacked) { if (playerBeingAttacked) {
Player player = (Player) event.getEntity(); Player player = (Player) event.getEntity();
if (player.getGameMode() == GameMode.CREATIVE && !toolStats.config.getBoolean("allow-creative")) {
return;
}
if (player.isBlocking()) { if (player.isBlocking()) {
double blockedDamage = -event.getDamage(EntityDamageEvent.DamageModifier.BLOCKING); double blockedDamage = -event.getDamage(EntityDamageEvent.DamageModifier.BLOCKING);
updateShieldDamage(player.getInventory(), blockedDamage); updateShieldDamage(player.getInventory(), blockedDamage);
@@ -85,6 +84,9 @@ public class EntityDamage implements Listener {
// player attacks something // player attacks something
if (playerAttacking) { if (playerAttacking) {
Player player = (Player) damager; Player player = (Player) damager;
if (player.getGameMode() == GameMode.CREATIVE && !toolStats.config.getBoolean("allow-creative")) {
return;
}
PlayerInventory playerAttackingInventory = player.getInventory(); PlayerInventory playerAttackingInventory = player.getInventory();
// make sure the item the player used is an item we want // make sure the item the player used is an item we want
if (!toolStats.itemChecker.isMeleeWeapon(playerAttackingInventory.getItemInMainHand().getType())) { if (!toolStats.itemChecker.isMeleeWeapon(playerAttackingInventory.getItemInMainHand().getType())) {
@@ -103,15 +105,15 @@ public class EntityDamage implements Listener {
if (modDied) { if (modDied) {
// player killed another player // player killed another player
if (playerBeingAttacked) { if (playerBeingAttacked) {
updateWeaponKills(playerAttackingInventory, "player"); updateWeaponKills(playerAttackingInventory, "player", null);
} else { } else {
// player kills a regular mob // player kills a regular mob
updateWeaponKills(playerAttackingInventory, "mob"); updateWeaponKills(playerAttackingInventory, "mob", mobBeingAttacked);
if (mobAttackedType == EntityType.WITHER) { if (mobAttackedType == EntityType.WITHER) {
updateBossesKilled(playerAttackingInventory, "wither"); updateBossesKilled(playerAttackingInventory, "wither", mobBeingAttacked);
} }
if (mobAttackedType == EntityType.ENDER_DRAGON) { if (mobAttackedType == EntityType.ENDER_DRAGON) {
updateBossesKilled(playerAttackingInventory, "enderdragon"); updateBossesKilled(playerAttackingInventory, "enderdragon", mobBeingAttacked);
} }
} }
} }
@@ -124,6 +126,9 @@ public class EntityDamage implements Listener {
if (damager instanceof Trident trident) { if (damager instanceof Trident trident) {
ProjectileSource source = trident.getShooter(); ProjectileSource source = trident.getShooter();
if (source instanceof Player player) { if (source instanceof Player player) {
if (player.getGameMode() == GameMode.CREATIVE && !toolStats.config.getBoolean("allow-creative")) {
return;
}
// update the trident's tracked damage // update the trident's tracked damage
updateTridentDamage(trident, finalDamage); updateTridentDamage(trident, finalDamage);
@@ -131,15 +136,15 @@ public class EntityDamage implements Listener {
if (modDied) { if (modDied) {
// if the trident killed a player, update the kills // if the trident killed a player, update the kills
if (playerBeingAttacked) { if (playerBeingAttacked) {
updateTridentKills(trident, "player"); updateTridentKills(trident, "player", null);
} else { } else {
// the trident killed a mob, update the kills // the trident killed a mob, update the kills
updateTridentKills(trident, "mob"); updateTridentKills(trident, "mob", mobBeingAttacked);
if (mobAttackedType == EntityType.WITHER) { if (mobAttackedType == EntityType.WITHER) {
updateBossesKilled(player.getInventory(), "wither"); updateBossesKilled(player.getInventory(), "wither", mobBeingAttacked);
} }
if (mobAttackedType == EntityType.ENDER_DRAGON) { if (mobAttackedType == EntityType.ENDER_DRAGON) {
updateBossesKilled(player.getInventory(), "enderdragon"); updateBossesKilled(player.getInventory(), "enderdragon", mobBeingAttacked);
} }
} }
} }
@@ -155,6 +160,9 @@ public class EntityDamage implements Listener {
// a player shot the arrow // a player shot the arrow
if (source instanceof Player shootingPlayer) { if (source instanceof Player shootingPlayer) {
if (shootingPlayer.getGameMode() == GameMode.CREATIVE && !toolStats.config.getBoolean("allow-creative")) {
return;
}
// update the player's bow damage // update the player's bow damage
updateBowDamage(shootingPlayer.getInventory(), finalDamage); updateBowDamage(shootingPlayer.getInventory(), finalDamage);
@@ -162,15 +170,15 @@ public class EntityDamage implements Listener {
if (modDied) { if (modDied) {
if (playerBeingAttacked) { if (playerBeingAttacked) {
// player killed another player with an arrow // player killed another player with an arrow
updateBowKills(shootingPlayer.getInventory(), "player"); updateBowKills(shootingPlayer.getInventory(), "player", null);
} else { } else {
// player killed mob with an arrow // player killed mob with an arrow
updateBowKills(shootingPlayer.getInventory(), "mob"); updateBowKills(shootingPlayer.getInventory(), "mob", mobBeingAttacked);
if (mobAttackedType == EntityType.WITHER) { if (mobAttackedType == EntityType.WITHER) {
updateBossesKilledByBow(shootingPlayer.getInventory(), "wither"); updateBossesKilledByBow(shootingPlayer.getInventory(), "wither", mobBeingAttacked);
} }
if (mobAttackedType == EntityType.ENDER_DRAGON) { if (mobAttackedType == EntityType.ENDER_DRAGON) {
updateBossesKilledByBow(shootingPlayer.getInventory(), "enderdragon"); updateBossesKilledByBow(shootingPlayer.getInventory(), "enderdragon", mobBeingAttacked);
} }
} }
} }
@@ -240,7 +248,7 @@ public class EntityDamage implements Listener {
} }
} }
private void updateBowKills(PlayerInventory playerInventory, String type) { private void updateBowKills(PlayerInventory playerInventory, String type, LivingEntity entity) {
ItemStack heldBow = toolStats.itemChecker.getBow(playerInventory); ItemStack heldBow = toolStats.itemChecker.getBow(playerInventory);
if (heldBow == null) { if (heldBow == null) {
return; return;
@@ -251,6 +259,21 @@ public class EntityDamage implements Listener {
if (type.equalsIgnoreCase("mob")) { if (type.equalsIgnoreCase("mob")) {
// player is shooting a mob // player is shooting a mob
if (toolStats.roseStacker != null) {
toolStats.roseStacker.countMobs(entity, count -> {
ItemMeta newBow = toolStats.itemLore.updateMobKills(heldBow, count);
if (newBow != null) {
if (isMain && isOffHand) {
playerInventory.getItemInMainHand().setItemMeta(newBow);
} else if (isMain) {
playerInventory.getItemInMainHand().setItemMeta(newBow);
} else if (isOffHand) {
playerInventory.getItemInOffHand().setItemMeta(newBow);
}
}
});
return;
}
ItemMeta newBow = toolStats.itemLore.updateMobKills(heldBow, 1); ItemMeta newBow = toolStats.itemLore.updateMobKills(heldBow, 1);
if (newBow != null) { if (newBow != null) {
if (isMain && isOffHand) { if (isMain && isOffHand) {
@@ -277,17 +300,32 @@ public class EntityDamage implements Listener {
} }
} }
private void updateTridentKills(Trident trident, String type) { private void updateTridentKills(Trident trident, String type, LivingEntity entity) {
ItemStack newTrident = trident.getItemStack(); ItemStack newTrident = trident.getItemStack();
ItemMeta newKills;
if (type.equalsIgnoreCase("player")) { if (type.equalsIgnoreCase("player")) {
newKills = toolStats.itemLore.updatePlayerKills(trident.getItemStack(), 1); ItemMeta newTridentMeta = toolStats.itemLore.updatePlayerKills(trident.getItemStack(), 1);
} else { if (newTridentMeta != null) {
newKills = toolStats.itemLore.updateMobKills(trident.getItemStack(), 1); newTrident.setItemMeta(newTridentMeta);
trident.setItemStack(newTrident);
}
return;
} }
if (newKills != null) { if (type.equalsIgnoreCase("mob")) {
newTrident.setItemMeta(newKills); if (toolStats.roseStacker != null) {
trident.setItemStack(newTrident); toolStats.roseStacker.countMobs(entity, count -> {
ItemMeta newTridentMeta = toolStats.itemLore.updateMobKills(newTrident, count);
if (newTridentMeta != null) {
newTrident.setItemMeta(newTridentMeta);
trident.setItemStack(newTrident);
}
});
return;
}
ItemMeta newTridentMeta = toolStats.itemLore.updateMobKills(trident.getItemStack(), 1);
if (newTridentMeta != null) {
newTrident.setItemMeta(newTridentMeta);
trident.setItemStack(newTrident);
}
} }
} }
@@ -308,29 +346,46 @@ public class EntityDamage implements Listener {
} }
} }
private void updateWeaponKills(PlayerInventory playerInventory, String type) { private void updateWeaponKills(PlayerInventory playerInventory, String type, LivingEntity entity) {
ItemStack heldWeapon = playerInventory.getItemInMainHand(); ItemStack heldWeapon = playerInventory.getItemInMainHand();
ItemMeta newHeldWeaponMeta = null;
if (type.equalsIgnoreCase("player")) { if (type.equalsIgnoreCase("player")) {
newHeldWeaponMeta = toolStats.itemLore.updatePlayerKills(heldWeapon, 1); ItemMeta newHeldWeaponMeta = toolStats.itemLore.updatePlayerKills(heldWeapon, 1);
if (newHeldWeaponMeta != null) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
}
return;
} }
if (type.equalsIgnoreCase("mob")) { if (type.equalsIgnoreCase("mob")) {
newHeldWeaponMeta = toolStats.itemLore.updateMobKills(heldWeapon, 1); if (toolStats.roseStacker != null) {
} toolStats.roseStacker.countMobs(entity, count -> {
if (newHeldWeaponMeta != null) { ItemStack currentHeldWeapon = playerInventory.getItemInMainHand();
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta); ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateMobKills(currentHeldWeapon, count);
if (newHeldWeaponMeta != null) {
currentHeldWeapon.setItemMeta(newHeldWeaponMeta);
}
});
} else {
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateMobKills(heldWeapon, 1);
if (newHeldWeaponMeta != null) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
}
}
} }
} }
private void updateBossesKilled(PlayerInventory playerInventory, String boss) { private void updateBossesKilled(PlayerInventory playerInventory, String boss, LivingEntity entity) {
ItemStack heldWeapon = playerInventory.getItemInMainHand(); ItemStack heldWeapon = playerInventory.getItemInMainHand();
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldWeapon, 1, boss); int count = 1;
if (toolStats.roseStacker != null) {
//count = toolStats.roseStacker.countMobs(entity);
}
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldWeapon, count, boss);
if (newHeldWeaponMeta != null) { if (newHeldWeaponMeta != null) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta); playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
} }
} }
private void updateBossesKilledByBow(PlayerInventory playerInventory, String boss) { private void updateBossesKilledByBow(PlayerInventory playerInventory, String boss, LivingEntity entity) {
ItemStack heldBow = toolStats.itemChecker.getBow(playerInventory); ItemStack heldBow = toolStats.itemChecker.getBow(playerInventory);
if (heldBow == null) { if (heldBow == null) {
return; return;
@@ -339,6 +394,22 @@ public class EntityDamage implements Listener {
boolean isMain = playerInventory.getItemInMainHand().getType() == Material.BOW || playerInventory.getItemInMainHand().getType() == Material.CROSSBOW; boolean isMain = playerInventory.getItemInMainHand().getType() == Material.BOW || playerInventory.getItemInMainHand().getType() == Material.CROSSBOW;
boolean isOffHand = playerInventory.getItemInOffHand().getType() == Material.BOW || playerInventory.getItemInOffHand().getType() == Material.CROSSBOW; boolean isOffHand = playerInventory.getItemInOffHand().getType() == Material.BOW || playerInventory.getItemInOffHand().getType() == Material.CROSSBOW;
if (toolStats.roseStacker != null) {
toolStats.roseStacker.countMobs(entity, count -> {
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldBow, count, boss);
if (newHeldWeaponMeta != null) {
if (isMain && isOffHand) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
} else if (isMain) {
playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta);
} else if (isOffHand) {
playerInventory.getItemInOffHand().setItemMeta(newHeldWeaponMeta);
}
}
});
return;
}
ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldBow, 1, boss); ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldBow, 1, boss);
if (newHeldWeaponMeta != null) { if (newHeldWeaponMeta != null) {
if (isMain && isOffHand) { if (isMain && isOffHand) {

View File

@@ -43,7 +43,7 @@ public class EntityDeath implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onDeath(EntityDeathEvent event) { public void onDeath(EntityDeathEvent event) {
LivingEntity livingEntity = event.getEntity(); LivingEntity livingEntity = event.getEntity();
if (livingEntity instanceof Player) { if (livingEntity instanceof Player) {

View File

@@ -39,14 +39,14 @@ import java.util.Map;
public class GenerateLoot implements Listener { public class GenerateLoot implements Listener {
private final ToolStats toolStats; private final ToolStats toolStats;
public Map<Inventory, Location> generatedInventory = new HashMap<>(); public final Map<Inventory, Location> generatedInventory = new HashMap<>();
public List<Location> droppedLootLocations = new ArrayList<>(); public final List<Location> droppedLootLocations = new ArrayList<>();
public GenerateLoot(ToolStats toolStats) { public GenerateLoot(ToolStats toolStats) {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onGenerateLoot(LootGenerateEvent event) { public void onGenerateLoot(LootGenerateEvent event) {
InventoryHolder inventoryHolder = event.getInventoryHolder(); InventoryHolder inventoryHolder = event.getInventoryHolder();
if (inventoryHolder == null) { if (inventoryHolder == null) {

View File

@@ -50,7 +50,7 @@ public class InventoryClose implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onClose(InventoryCloseEvent event) { public void onClose(InventoryCloseEvent event) {
if (toolStats.generateLoot.generatedInventory.isEmpty()) { if (toolStats.generateLoot.generatedInventory.isEmpty()) {
return; return;
@@ -106,7 +106,7 @@ public class InventoryClose implements Listener {
}, null, 1); }, null, 1);
} }
if (holder instanceof Container container) { if (holder instanceof Container) {
Chunk chestChunk = chestLocation.getChunk(); Chunk chestChunk = chestLocation.getChunk();
Bukkit.getRegionScheduler().runDelayed(toolStats, chestLocation.getWorld(), chestChunk.getX(), chestChunk.getZ(), scheduledTask -> { Bukkit.getRegionScheduler().runDelayed(toolStats, chestLocation.getWorld(), chestChunk.getX(), chestChunk.getZ(), scheduledTask -> {
BlockState blockState = chestLocation.getWorld().getBlockAt(chestLocation).getState(); BlockState blockState = chestLocation.getWorld().getBlockAt(chestLocation).getState();

View File

@@ -42,12 +42,8 @@ public class InventoryOpen implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onOpen(InventoryOpenEvent event) { public void onOpen(InventoryOpenEvent event) {
if (event.isCancelled()) {
return;
}
Inventory inventory = event.getInventory(); Inventory inventory = event.getInventory();
InventoryHolder holder = inventory.getHolder(); InventoryHolder holder = inventory.getHolder();
boolean isBlockInventory = holder instanceof BlockInventoryHolder || holder instanceof DoubleChest; boolean isBlockInventory = holder instanceof BlockInventoryHolder || holder instanceof DoubleChest;

View File

@@ -47,11 +47,8 @@ public class PickupItem implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPickup(EntityPickupItemEvent event) { public void onPickup(EntityPickupItemEvent event) {
if (event.isCancelled()) {
return;
}
Entity entity = event.getEntity(); Entity entity = event.getEntity();
if (entity instanceof Player player) { if (entity instanceof Player player) {
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {

View File

@@ -34,7 +34,7 @@ public class PlayerDrop implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onDrop(PlayerDropItemEvent event) { public void onDrop(PlayerDropItemEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {

View File

@@ -46,11 +46,8 @@ public class PlayerFish implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onFish(PlayerFishEvent event) { public void onFish(PlayerFishEvent event) {
if (event.isCancelled()) {
return;
}
// only listen to when a player catches a fish // only listen to when a player catches a fish
if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) { if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) {
return; return;

View File

@@ -33,9 +33,13 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
public class PlayerInteract implements Listener { public class PlayerInteract implements Listener {
@@ -50,7 +54,7 @@ public class PlayerInteract implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onInteract(PlayerInteractEvent event) { public void onInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return; return;
@@ -74,11 +78,35 @@ public class PlayerInteract implements Listener {
Inventory holderInventory = holder.getInventory(); Inventory holderInventory = holder.getInventory();
openedChests.add(block); openedChests.add(block);
chestInventories.add(holderInventory); chestInventories.add(holderInventory);
Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> openedChests.remove(block), 20); Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, _ -> openedChests.remove(block), 20);
}
// player right-clicked a log
String blockType = block.getType().toString().toLowerCase(Locale.ROOT);
if (blockType.endsWith("_log") && !blockType.contains("stripped")) {
PlayerInventory playerInventory = player.getInventory();
ItemStack axe = toolStats.itemChecker.getAxe(playerInventory);
// not holding an axe
if (axe == null) {
return;
}
ItemMeta newAxe = toolStats.itemLore.updateLogsStripped(axe, 1);
if (newAxe != null) {
boolean isMain = playerInventory.getItemInMainHand().getType().toString().endsWith("_AXE");
boolean isOffHand = playerInventory.getItemInOffHand().getType().toString().endsWith("_AXE");
if (isMain && isOffHand) {
playerInventory.getItemInMainHand().setItemMeta(newAxe);
} else if (isMain) {
playerInventory.getItemInMainHand().setItemMeta(newAxe);
} else if (isOffHand) {
playerInventory.getItemInOffHand().setItemMeta(newAxe);
}
}
} }
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onInteract(PlayerInteractEntityEvent event) { public void onInteract(PlayerInteractEntityEvent event) {
Entity clicked = event.getRightClicked(); Entity clicked = event.getRightClicked();
Player player = event.getPlayer(); Player player = event.getPlayer();
@@ -91,7 +119,7 @@ public class PlayerInteract implements Listener {
Inventory mineCartInventory = storageMinecart.getInventory(); Inventory mineCartInventory = storageMinecart.getInventory();
mineCartChestInventories.add(mineCartInventory); mineCartChestInventories.add(mineCartInventory);
openedMineCarts.add(storageMinecart); openedMineCarts.add(storageMinecart);
Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> openedMineCarts.remove(storageMinecart), 20); Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, _ -> openedMineCarts.remove(storageMinecart), 20);
} }
} }
} }

View File

View File

@@ -39,7 +39,7 @@ public class PlayerMove implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onMove(PlayerMoveEvent event) { public void onMove(PlayerMoveEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (!toolStats.configTools.checkWorld(player.getWorld().getName())) { if (!toolStats.configTools.checkWorld(player.getWorld().getName())) {

View File

View File

@@ -25,7 +25,6 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
;
public class ProjectileShoot implements Listener { public class ProjectileShoot implements Listener {

View File

@@ -39,7 +39,7 @@ public class SheepShear implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onShear(PlayerInteractEntityEvent event) { public void onShear(PlayerInteractEntityEvent event) {
if (event.isCancelled()) { if (event.isCancelled()) {
return; return;
@@ -62,6 +62,11 @@ public class SheepShear implements Listener {
return; return;
} }
// make sure they are not a baby
if (!sheep.isAdult()) {
return;
}
// make sure the sheep is not sheared // make sure the sheep is not sheared
if (sheep.isSheared()) { if (sheep.isSheared()) {
return; return;

2
src/main/java/lol/hyper/toolstats/events/ShootBow.java Normal file → Executable file
View File

@@ -38,7 +38,7 @@ public class ShootBow implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onShoot(EntityShootBowEvent event) { public void onShoot(EntityShootBowEvent event) {
Entity shooter = event.getEntity(); Entity shooter = event.getEntity();
// only listen for players // only listen for players

View File

@@ -47,9 +47,9 @@ public class VillagerTrade implements Listener {
this.toolStats = toolStats; this.toolStats = toolStats;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onTrade(InventoryClickEvent event) { public void onTrade(InventoryClickEvent event) {
if (event.isCancelled() || event.getCurrentItem() == null) { if (event.getCurrentItem() == null) {
return; return;
} }
Inventory inventory = event.getClickedInventory(); Inventory inventory = event.getClickedInventory();

View File

@@ -0,0 +1,79 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.support.rosestacker;
import dev.rosewood.rosestacker.api.RoseStackerAPI;
import dev.rosewood.rosestacker.stack.StackedEntity;
import lol.hyper.toolstats.ToolStats;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity;
import java.util.function.Consumer;
public class RoseStacker {
private final ToolStats toolStats;
private final RoseStackerAPI rsAPI;
public RoseStacker(ToolStats toolStats) {
this.toolStats = toolStats;
this.rsAPI = RoseStackerAPI.getInstance();
}
public void countMobs(LivingEntity entity, Consumer<Integer> callback) {
if (!rsAPI.isEntityStacked(entity)) {
// if the entity is not stacked, ignore
callback.accept(1);
return;
}
StackedEntity stackedEntity = rsAPI.getStackedEntity(entity);
if (stackedEntity == null) {
callback.accept(1);
return;
}
int before = stackedEntity.getStackSize();
boolean killAll = stackedEntity.getStackSettings().shouldKillEntireStackOnDeath();
// if we kill the entire stack, add the entire stack to the count
if (killAll) {
callback.accept(before);
return;
}
Location stackedLocation = stackedEntity.getLocation();
Chunk stackedChunk = stackedEntity.getLocation().getChunk();
// check the stack size after a tick to see the difference
Bukkit.getRegionScheduler().runDelayed(toolStats, stackedLocation.getWorld(), stackedChunk.getX(), stackedChunk.getZ(), _ -> {
int after = stackedEntity.getStackSize();
int difference = before - after;
// if the diff goes negative, we killed more than the stack
// we killed the entire stack, so return the size
if (difference <= 0) {
difference = before;
}
toolStats.logger.info("before: {}", before);
toolStats.logger.info("after: {}", after);
toolStats.logger.info("difference: {}", difference);
callback.accept(difference);
}, 1);
}
}

0
src/main/java/lol/hyper/toolstats/tools/HashMaker.java Normal file → Executable file
View File

View File

@@ -306,6 +306,35 @@ public class ItemChecker {
return null; return null;
} }
/**
* Get the player's axe.
*
* @param inventory Their inventory.
* @return Their axe, either main or offhand.
*/
public @Nullable ItemStack getAxe(PlayerInventory inventory) {
ItemStack main = inventory.getItemInMainHand();
ItemStack offHand = inventory.getItemInOffHand();
boolean isMain = main.getType().toString().endsWith("_AXE");
boolean isOffHand = offHand.getType().toString().endsWith("_AXE");
// if the player is holding an axe in their main hand, use that one
// if the axe is in their offhand instead, use that one after checking main hand
// Minecraft prioritizes main hand if the player holds in both hands
if (isMain && isOffHand) {
return main;
}
if (isMain) {
return main;
}
if (isOffHand) {
return offHand;
}
return null;
}
/** /**
* Checks the keys of the item and returns the tokens we should add. * Checks the keys of the item and returns the tokens we should add.
* If the server swaps token systems this should allow compatability. * If the server swaps token systems this should allow compatability.
@@ -363,6 +392,9 @@ public class ItemChecker {
if (container.has(toolStats.toolStatsKeys.getTridentThrows())) { if (container.has(toolStats.toolStatsKeys.getTridentThrows())) {
tokens.add("trident-throws"); tokens.add("trident-throws");
} }
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
tokens.add("logs-stripped");
}
if (tokens.isEmpty()) { if (tokens.isEmpty()) {
return null; return null;
} }

123
src/main/java/lol/hyper/toolstats/tools/ItemLore.java Normal file → Executable file
View File

@@ -1299,7 +1299,7 @@ public class ItemLore {
if (criticalStrikes == null) { if (criticalStrikes == null) {
criticalStrikes = 0; criticalStrikes = 0;
toolStats.logger.warn("{} does not have valid fish-caught set! Resting to zero. This should NEVER happen.", clone); toolStats.logger.warn("{} does not have valid critical-strikes set! Resting to zero. This should NEVER happen.", clone);
} }
container.set(toolStats.toolStatsKeys.getCriticalStrikes(), PersistentDataType.INTEGER, criticalStrikes + add); container.set(toolStats.toolStatsKeys.getCriticalStrikes(), PersistentDataType.INTEGER, criticalStrikes + add);
@@ -1397,7 +1397,7 @@ public class ItemLore {
if (tridentThrows == null) { if (tridentThrows == null) {
tridentThrows = 0; tridentThrows = 0;
toolStats.logger.warn("{} does not have valid fish-caught set! Resting to zero. This should NEVER happen.", clone); toolStats.logger.warn("{} does not have valid trident-throws set! Resting to zero. This should NEVER happen.", clone);
} }
container.set(toolStats.toolStatsKeys.getTridentThrows(), PersistentDataType.INTEGER, tridentThrows + add); container.set(toolStats.toolStatsKeys.getTridentThrows(), PersistentDataType.INTEGER, tridentThrows + add);
@@ -1410,7 +1410,104 @@ public class ItemLore {
} }
List<Component> newLore = updateItemLore(meta, oldLine, newLine); List<Component> newLore = updateItemLore(meta, oldLine, newLine);
meta.lore(newLore); meta.lore(newLore);
toolStats.logger.info(meta.toString()); return meta;
}
/**
* Add x to logs stripped.
*
* @param axe The axe used.
*/
public ItemMeta updateLogsStripped(ItemStack axe, int add) {
ItemStack clone = axe.clone();
ItemMeta meta = clone.getItemMeta();
if (meta == null) {
toolStats.logger.warn("{} does NOT have any meta! Unable to update stats.", clone);
return null;
}
PersistentDataContainer container = meta.getPersistentDataContainer();
// if it's disabled, don't update the stats
// check to see if the item has the stats, remove them if it does
if (!toolStats.config.getBoolean("enabled.logs-stripped")) {
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (logsStripped == null) {
return null;
}
container.remove(toolStats.toolStatsKeys.getLogsStripped());
// remove the applied token if this stat is disabled
if (container.has(toolStats.toolStatsKeys.getTokenApplied())) {
String appliedTokens = container.get(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING);
if (appliedTokens != null) {
// remove the token from the list
// if the list is empty, remove the PDC
// otherwise set the PDC back with the new list
List<String> newTokens = toolStats.itemChecker.removeToken(appliedTokens, "logs-stripped");
if (!newTokens.isEmpty()) {
container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, String.join(",", newTokens));
} else {
container.remove(toolStats.toolStatsKeys.getTokenApplied());
}
}
}
if (meta.hasLore()) {
String oldLogsStripped = toolStats.numberFormat.formatInt(logsStripped);
Component lineToRemove = toolStats.configTools.formatLore("logs-stripped", "{logs}", oldLogsStripped);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
return meta;
}
return null;
}
// check for tokens
boolean validToken = toolStats.itemChecker.checkTokens(container, "logs-stripped");
// check for tokens
if (toolStats.config.getBoolean("tokens.enabled")) {
// if the item has stats but no token, add the token
if (container.has(toolStats.toolStatsKeys.getLogsStripped()) && !validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, newTokens);
}
}
// the item does not have a valid token
if (!validToken) {
return null;
}
} else {
if (!validToken) {
String newTokens = toolStats.itemChecker.addTokensToExisting(clone);
if (newTokens != null) {
container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, newTokens);
}
}
}
Integer logsStripped = 0;
if (container.has(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER)) {
logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
}
if (logsStripped == null) {
logsStripped = 0;
toolStats.logger.warn("{} does not have valid logs-stripped set! Resting to zero. This should NEVER happen.", clone);
}
container.set(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER, logsStripped + add);
String oldLogsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped);
String newLogsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped + add);
Component oldLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", oldLogsStrippedFormatted);
Component newLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", newLogsStrippedFormatted);
if (oldLine == null || newLine == null) {
return null;
}
List<Component> newLore = updateItemLore(meta, oldLine, newLine);
meta.lore(newLore);
return meta; return meta;
} }
@@ -1668,6 +1765,26 @@ public class ItemLore {
finalItem.setItemMeta(meta); finalItem.setItemMeta(meta);
} }
} }
if (container.has(toolStats.toolStatsKeys.getTridentThrows())) {
Integer tridentThrows = container.get(toolStats.toolStatsKeys.getTridentThrows(), PersistentDataType.INTEGER);
if (tridentThrows != null) {
container.remove(toolStats.toolStatsKeys.getTridentThrows());
String tridentThrowsFormatted = toolStats.numberFormat.formatInt(tridentThrows);
Component lineToRemove = toolStats.configTools.formatLore("trident-throws", "{times}", tridentThrowsFormatted);
meta.lore(removeLore(meta.lore(), lineToRemove));
finalItem.setItemMeta(meta);
}
}
if (container.has(toolStats.toolStatsKeys.getLogsStripped())) {
Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER);
if (logsStripped != null) {
container.remove(toolStats.toolStatsKeys.getLogsStripped());
String logsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped);
Component lineToRemove = toolStats.configTools.formatLore("logs-stripped", "{logs}", logsStrippedFormatted);
meta.lore(removeLore(meta.lore(), lineToRemove));
finalItem.setItemMeta(meta);
}
}
if (removeMeta) { if (removeMeta) {
Integer origin = null; Integer origin = null;
if (container.has(toolStats.toolStatsKeys.getOriginType())) { if (container.has(toolStats.toolStatsKeys.getOriginType())) {

View File

8
src/main/java/lol/hyper/toolstats/tools/TokenData.java Normal file → Executable file
View File

@@ -153,6 +153,13 @@ public class TokenData {
tridentThrowsRecipe.setIngredient('S', Material.PRISMARINE_SHARD); tridentThrowsRecipe.setIngredient('S', Material.PRISMARINE_SHARD);
recipes.add(tridentThrowsRecipe); recipes.add(tridentThrowsRecipe);
NamespacedKey logsStrippedKey = new NamespacedKey(toolStats, "logs-stripped-token");
ShapedRecipe logsStrippedRecipe = new ShapedRecipe(logsStrippedKey, createToken("logs-stripped"));
logsStrippedRecipe.shape(" P ", "PSP", " P ");
logsStrippedRecipe.setIngredient('P', Material.PAPER);
logsStrippedRecipe.setIngredient('S', Material.WOODEN_AXE);
recipes.add(logsStrippedRecipe);
tokenTypes.add("crops-mined"); tokenTypes.add("crops-mined");
tokenTypes.add("blocks-mined"); tokenTypes.add("blocks-mined");
tokenTypes.add("damage-taken"); tokenTypes.add("damage-taken");
@@ -169,6 +176,7 @@ public class TokenData {
tokenTypes.add("enderdragon-kills"); tokenTypes.add("enderdragon-kills");
tokenTypes.add("critical-strikes"); tokenTypes.add("critical-strikes");
tokenTypes.add("trident-throws"); tokenTypes.add("trident-throws");
tokenTypes.add("logs-stripped");
} }
public Set<ShapedRecipe> getRecipes() { public Set<ShapedRecipe> getRecipes() {

View File

@@ -37,6 +37,7 @@ public class ToolStatsKeys {
private NamespacedKey criticalStrikes; private NamespacedKey criticalStrikes;
private NamespacedKey tridentThrows; private NamespacedKey tridentThrows;
private NamespacedKey originType; private NamespacedKey originType;
private NamespacedKey logsStripped;
public void make() { public void make() {
itemOwner = new NamespacedKey(toolStats, "owner"); itemOwner = new NamespacedKey(toolStats, "owner");
@@ -61,6 +62,7 @@ public class ToolStatsKeys {
criticalStrikes = new NamespacedKey(toolStats, "critical-strikes"); criticalStrikes = new NamespacedKey(toolStats, "critical-strikes");
tridentThrows = new NamespacedKey(toolStats, "trident-throws"); tridentThrows = new NamespacedKey(toolStats, "trident-throws");
originType = new NamespacedKey(toolStats, "origin"); originType = new NamespacedKey(toolStats, "origin");
logsStripped = new NamespacedKey(toolStats, "logs-stripped");
// save which stat can be used by a reset token // save which stat can be used by a reset token
tokenKeys.add(blocksMined); tokenKeys.add(blocksMined);
@@ -76,6 +78,7 @@ public class ToolStatsKeys {
tokenKeys.add(enderDragonKills); tokenKeys.add(enderDragonKills);
tokenKeys.add(criticalStrikes); tokenKeys.add(criticalStrikes);
tokenKeys.add(tridentThrows); tokenKeys.add(tridentThrows);
tokenKeys.add(logsStripped);
} }
public NamespacedKey getItemOwner() { public NamespacedKey getItemOwner() {
@@ -162,6 +165,10 @@ public class ToolStatsKeys {
return tridentThrows; return tridentThrows;
} }
public NamespacedKey getLogsStripped() {
return logsStripped;
}
/** /**
* Stores how an item was created. * Stores how an item was created.
* 0 = crafted. * 0 = crafted.

View File

@@ -43,6 +43,7 @@ public class ConfigUpdater {
case 13 -> new Version14(toolStats).update(); // 13 to 14 case 13 -> new Version14(toolStats).update(); // 13 to 14
case 14 -> new Version15(toolStats).update(); // 14 to 15 case 14 -> new Version15(toolStats).update(); // 14 to 15
case 15 -> new Version16(toolStats).update(); // 15 to 16 case 15 -> new Version16(toolStats).update(); // 15 to 16
case 16 -> new Version17(toolStats).update(); // 16 to 17
} }
} }
} }

View File

View File

View File

View File

View File

View File

@@ -18,7 +18,6 @@
package lol.hyper.toolstats.tools.config.versions; package lol.hyper.toolstats.tools.config.versions;
import lol.hyper.toolstats.ToolStats; import lol.hyper.toolstats.ToolStats;
import org.bukkit.configuration.ConfigurationSection;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;

View File

@@ -0,0 +1,82 @@
/*
* This file is part of ToolStats.
*
* ToolStats is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ToolStats is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ToolStats. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.toolstats.tools.config.versions;
import lol.hyper.toolstats.ToolStats;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class Version17 {
private final ToolStats toolStats;
/**
* Used for updating from version 16 to 17.
*
* @param toolStats ToolStats instance.
*/
public Version17(ToolStats toolStats) {
this.toolStats = toolStats;
}
/**
* Perform the config update.
*/
public void update() {
// save the old config first
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config-16.yml");
} catch (IOException exception) {
toolStats.logger.error("Unable to save config-16.yml!", exception);
}
toolStats.logger.info("Updating config.yml to version 17.");
toolStats.config.set("config-version", 17);
toolStats.logger.info("Adding new token to config: logs-stripped");
toolStats.config.set("tokens.data.logs-stripped.title", "&7ToolStats: &8Logs Stripped Token");
toolStats.config.set("tokens.data.logs-stripped.lore", List.of(
"&8Combine with an axe in an anvil to track logs stripped.",
"&8Uses &7{levels} &8level."
));
toolStats.config.set("tokens.data.logs-stripped.levels", 1);
toolStats.config.set("tokens.data.logs-stripped.material", "PAPER");
toolStats.config.set("tokens.data.logs-stripped.custom-model-data.enabled", false);
toolStats.config.set("tokens.data.logs-stripped.custom-model-data.type", "float");
toolStats.config.set("tokens.data.logs-stripped.custom-model-data.value", 1001);
toolStats.logger.info("Adding enabled.logs-stripped");
toolStats.config.set("enabled.logs-stripped", true);
toolStats.logger.info("Adding messages.logs-stripped");
toolStats.config.set("messages.logs-stripped", "&7Logs stripped: &8{logs}");
// save the config and reload it
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config.yml");
} catch (IOException exception) {
toolStats.logger.error("Unable to save config.yml!", exception);
}
toolStats.loadConfig();
toolStats.logger.info("Config has been updated to version 17. A copy of version 16 has been saved as config-16.yml");
}
}

View File

View File

View File

View File

15
src/main/resources/config.yml Normal file → Executable file
View File

@@ -180,6 +180,17 @@ tokens:
enabled: false enabled: false
type: float type: float
value: 1001 value: 1001
logs-stripped:
title: "&7ToolStats: &8Logs Stripped Token"
lore:
- "&8Combine with an axe in an anvil to track logs stripped."
- "&8Uses &7{levels} &8level."
levels: 1
material: PAPER
custom-model-data:
enabled: false
type: float
value: 1001
enabled: enabled:
# Will show "Crafted by <player>" # Will show "Crafted by <player>"
@@ -349,6 +360,7 @@ enabled:
crops-harvested: true crops-harvested: true
critical-strikes: true critical-strikes: true
trident-throws: true trident-throws: true
logs-stripped: true
messages: messages:
crafted: crafted:
@@ -386,6 +398,7 @@ messages:
damage-done: "&7Damage done: &8{damage}" damage-done: "&7Damage done: &8{damage}"
critical-strikes: "&7Critical strikes: &8{strikes}" critical-strikes: "&7Critical strikes: &8{strikes}"
trident-throws: "&7Times thrown: &8{times}" trident-throws: "&7Times thrown: &8{times}"
logs-stripped: "&7Logs stripped: &8{logs}"
# Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html # Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html
mobs: mobs:
ZOMBIE: "Zombie" ZOMBIE: "Zombie"
@@ -424,4 +437,4 @@ world-limit:
- world_1 - world_1
- world_2 - world_2
config-version: 15 config-version: 17

View File

@@ -1,7 +1,7 @@
name: ToolStats name: ToolStats
version: ${project.version} version: ${version}
main: lol.hyper.toolstats.ToolStats main: lol.hyper.toolstats.ToolStats
api-version: 1.21 api-version: '26.1.1'
folia-supported: true folia-supported: true
author: hyperdefined author: hyperdefined
description: Track various tool stats! description: Track various tool stats!
@@ -30,3 +30,9 @@ permissions:
toolstats.remove: toolstats.remove:
description: Allows the usage of /toolstats remove. description: Allows the usage of /toolstats remove.
default: op default: op
dependencies:
server:
RoseStacker:
load: BEFORE
required: false