diff --git a/Makefile b/Makefile index 0218e69..90a194c 100644 --- a/Makefile +++ b/Makefile @@ -4,36 +4,32 @@ all: releasejar RUST_SOURCE_FILES := $(shell find lib/src -type f) # Release - Linux x86_64 Xenial -lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-xenial.so: ${RUST_SOURCE_FILES} +lib/target/x86_64-unknown-linux-gnu/release/libskinfixer.so: ${RUST_SOURCE_FILES} + mkdir -p lib/target/x86_64-unknown-linux-gnu/release docker run -v "${CURDIR}/lib/:/code/" docker-registry.k8s.array21.dev/rust-amd64-xenial-builder release - mv lib/target/x86_64-unknown-linux-gnu/release/libskinfixer.so lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-xenial.so - -# Release - Linux x86_64 Focal -lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-focal.so: ${RUST_SOURCE_FILES} - docker run -v "${CURDIR}/lib/:/code" docker-registry.k8s.array21.dev/rust-amd64-focal-builder release - mv lib/target/x86_64-unknown-linux-gnu/release/libskinfixer.so lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-focal.so - -# Release - Linux x86_64 Bionic -lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-bionic.so: ${RUST_SOURCE_FILES} - docker run -v "${CURDIR}/lib/:/code" docker-registry.k8s.array21.dev/rust-amd64-bionic-builder release - mv lib/target/x86_64-unknown-linux-gnu/release/libskinfixer.so lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-bionic.so # Release - Linux aarch64 lib/target/aarch64-unknown-linux-gnu/release/libskinfixer.so: ${RUST_SOURCE_FILES} + mkdir -p lib/target/aarch64-unknown-linux-gnu/release docker run -v "${CURDIR}/lib/:/code/" docker-registry.k8s.array21.dev/rust-aarch64-focal-builder release # Release - Linux armhf lib/target/arm-unknown-linux-gnueabihf/release/libskinfixer.so: ${RUST_SOURCE_FILES} + mkdir -p lib/target/arm-unknown-linux-gnueabihf/release docker run -v "${CURDIR}/lib/:/code/" docker-registry.k8s.array21.dev/rust-armhf-focal-builder release # Release - Windows x86_64 lib/target/x86_64-pc-windows-gnu/release/libskinfixer.dll: ${RUST_SOURCE_FILES} + sudo chown ${USER}:${USER} -R lib/ + sudo chmod a+rwx -R lib/ + mkdir -p lib/target/x86_64-pc-windows-gnu/release cd lib; \ cargo build --release --target x86_64-pc-windows-gnu cp lib/target/x86_64-pc-windows-gnu/release/skinfixer.dll lib/target/x86_64-pc-windows-gnu/release/libskinfixer.dll # Release - Darwin x86_64 lib/target/x86_64-apple-darwin/release/libskinfixer.dylib: ${RUST_SOURCE_FILES} + mkdir -p lib/target/x86_64-apple-darwin/release docker run -v "${CURDIR}/lib/:/code/" docker-registry.k8s.array21.dev/rust-amd64-darwin-builder release # Debug - Linux x86_64 @@ -50,6 +46,7 @@ lib/target/arm-unknown-linux-gnueabihf/debug/libskinfixer.so: ${RUST_SOURCE_FILE # Debug - Windows x86_64 lib/target/x86_64-pc-windows-gnu/debug/libskinfixer.dll: ${RUST_SOURCE_FILES} + sudo chmod -R a+rwx lib/ cd lib; \ cargo build --target x86_64-pc-windows-gnu cp lib/target/x86_64-pc-windows-gnu/debug/skinfixer.dll lib/target/x86_64-pc-windows-gnu/debug/libskinfixer.dll diff --git a/build.gradle b/build.gradle index 321726f..b0ff4bf 100644 --- a/build.gradle +++ b/build.gradle @@ -18,11 +18,11 @@ allprojects { mavenCentral() maven{ url "https://hub.spigotmc.org/nexus/content/repositories/snapshots" } maven{ url "https://oss.sonatype.org/content/repositories/snapshots" } - maven{ url 'https://jitpack.io' } maven { name 'm2-dv8tion' url 'https://m2.dv8tion.net/releases' } + mavenLocal() } processResources { @@ -35,9 +35,9 @@ allprojects { dependencies { compileOnly 'org.spigotmc:spigot-api:1.17-R0.1-SNAPSHOT' - implementation 'com.github.TheDutchMC:HttpLib:1.1' + implementation 'dev.array21:httplib:1.2.2' implementation 'dev.array21:classvalidator:1.0.0' - implementation 'dev.array21:pluginstatlib:1.0.5' + implementation 'dev.array21:bukkit-reflection-util:1.1.1' // JDA and related dependencies implementation 'org.slf4j:slf4j-log4j12:1.7.29' @@ -81,33 +81,16 @@ task copyDependenciesDebug() { } task copyDependenciesRelease() { - dependsOn('copyLinuxLibraryReleaseX86Focal') - dependsOn('copyLinuxLibraryReleaseX86Bionic') - dependsOn('copyLinuxLibraryReleaseX86Xenial') - + dependsOn('copyLinuxLibraryReleaseX86') dependsOn('copyLinuxLibraryReleaseAarch64') dependsOn('copyLinuxLibraryReleaseArmhf') - dependsOn('copyDarwinLibraryReleaseX86') - dependsOn('copyWindowsLibraryRelease') } -// Linux X86 Focal - Release -task copyLinuxLibraryReleaseX86Focal(type: Copy) { - from "$rootDir/lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-focal.so" - into "$buildDir/resources/main/x86_64/linux" -} - -// Linux X86 Bionic - Release -task copyLinuxLibraryReleaseX86Bionic(type: Copy) { - from "$rootDir/lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-bionic.so" - into "$buildDir/resources/main/x86_64/linux" -} - // Linux X86 Xenial - Release -task copyLinuxLibraryReleaseX86Xenial(type: Copy) { - from "$rootDir/lib/target/x86_64-unknown-linux-gnu/release/libskinfixer-xenial.so" +task copyLinuxLibraryReleaseX86(type: Copy) { + from "$rootDir/lib/target/x86_64-unknown-linux-gnu/release/libskinfixer.so" into "$buildDir/resources/main/x86_64/linux" } diff --git a/gradle.properties b/gradle.properties index cd7f99f..1d8c8ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -pluginVersion = 1.7.1 \ No newline at end of file +pluginVersion = 1.7.2 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c..7454180 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0f80bbf..e750102 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..1b6c787 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# 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. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# 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/master/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 -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +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 -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # 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"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +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 - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +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 @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 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" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + 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 @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -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" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +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 - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + 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 - i=`expr $i + 1` + # 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 - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# 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 \ + "$@" + +# 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. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/lib/.gitignore b/lib/.gitignore old mode 100644 new mode 100755 diff --git a/lib/Cargo.toml b/lib/Cargo.toml old mode 100644 new mode 100755 index 2631707..9ba124e --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -11,7 +11,6 @@ path = "src/lib.rs" [dependencies] jni = "0.19.0" -mysql = "21.0.0" postgres = "0.19.1" bincode = "1.3.3" lazy_static = "1.4.0" @@ -25,5 +24,12 @@ version = "1.0.126" features = ["derive"] [dependencies.typenum] -version = "1.13.0" -features = ["no_std"] \ No newline at end of file +version = "1.14.0" +features = ["no_std"] + +[dependencies.mysql] +version = "21.0.2" +default-features = false + +[patch.crates-io.mysql] +git = "https://github.com/TheDutchMC/rust-mysql-simple" \ No newline at end of file diff --git a/lib/src/config.rs b/lib/src/config.rs old mode 100644 new mode 100755 diff --git a/lib/src/jni/del_skin_profile.rs b/lib/src/jni/del_skin_profile.rs old mode 100644 new mode 100755 diff --git a/lib/src/jni/get_skin_profile.rs b/lib/src/jni/get_skin_profile.rs old mode 100644 new mode 100755 diff --git a/lib/src/jni/init.rs b/lib/src/jni/init.rs old mode 100644 new mode 100755 diff --git a/lib/src/jni/mod.rs b/lib/src/jni/mod.rs old mode 100644 new mode 100755 diff --git a/lib/src/jni/set_skin_profile.rs b/lib/src/jni/set_skin_profile.rs old mode 100644 new mode 100755 diff --git a/lib/src/lib.rs b/lib/src/lib.rs old mode 100644 new mode 100755 diff --git a/src/main/java/dev/array21/skinfixer/SkinChangeHandler.java b/src/main/java/dev/array21/skinfixer/SkinChangeHandler.java index dbcb389..07d771b 100644 --- a/src/main/java/dev/array21/skinfixer/SkinChangeHandler.java +++ b/src/main/java/dev/array21/skinfixer/SkinChangeHandler.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.UUID; +import dev.array21.bukkitreflectionlib.ReflectionUtil; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -19,7 +20,6 @@ import dev.array21.skinfixer.apis.gson.GetSkinResponse; import dev.array21.skinfixer.language.LangHandler; import dev.array21.skinfixer.storage.SkinData; -import dev.array21.skinfixer.util.ReflectionUtil; import dev.array21.skinfixer.util.Triple; import net.md_5.bungee.api.ChatColor; @@ -115,7 +115,161 @@ private void changeSkin(String skinValue, String skinSignature, UUID caller, boo player.sendMessage(ChatColor.GOLD + LangHandler.model.skinApplied); } } - + + private void applySkin1_16(Player player, String skinValue, String skinSignature) throws Exception { + Class craftPlayerClass = ReflectionUtil.getBukkitClass("entity.CraftPlayer"); + Object entityPlayer = ReflectionUtil.invokeMethod(craftPlayerClass, player, "getHandle"); + Class entityHumanClass = ReflectionUtil.getNmsClass("EntityHuman"); + + Object gameProfile = ReflectionUtil.invokeMethod(entityHumanClass, entityPlayer, "getProfile"); + Object propertyMap = ReflectionUtil.invokeMethod(gameProfile, "getProperties"); + + //Check if the PropertyMap contains the 'textures' property + //If so remove it + //The containsKey method is in the ForwardingMultimap class, which PropertyMap extends + Class forwardingMultimapClass = com.google.common.collect.ForwardingMultimap.class; + Boolean containsKeyTextures = (Boolean) ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "containsKey", new Class[] { Object.class }, new Object[] { "textures" }); + if(containsKeyTextures) { + Object textures = ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "get", new Class[] { Object.class }, new Object[] { "textures" }); + Object texturesIter = ReflectionUtil.invokeMethod(Collection.class, textures, "iterator"); + Object iterNext = ReflectionUtil.invokeMethod(texturesIter, "next"); + + ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "remove", new Class[] { Object.class, Object.class }, new Object[] { "textures", iterNext }); + } + + //Create a new 'textures' Property with the new skinValue and skinSignature + //and put it in the PropertyMap + Class propertyClass = Class.forName("com.mojang.authlib.properties.Property"); + Object newProperty = ReflectionUtil.invokeConstructor(propertyClass, "textures", skinValue, skinSignature); + ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "put", new Class[] { Object.class, Object.class }, new Object[] { "textures", newProperty }); + + Class packetPlayOutPlayerInfoClass = ReflectionUtil.getNmsClass("PacketPlayOutPlayerInfo"); + + Object removePlayerEnumConstant = ReflectionUtil.getEnum(packetPlayOutPlayerInfoClass, "EnumPlayerInfoAction", "REMOVE_PLAYER"); + Object addPlayerEnumConstant = ReflectionUtil.getEnum(packetPlayOutPlayerInfoClass, "EnumPlayerInfoAction", "ADD_PLAYER"); + + //Create an Array of EntityPlayer with size = 1 and add our player to it + Object entityPlayerArr = Array.newInstance(entityPlayer.getClass(), 1); + Array.set(entityPlayerArr, 0, entityPlayer); + + Object packetPlayOutPlayerInfoRemovePlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, removePlayerEnumConstant, entityPlayerArr); + Object packetPlayOutPlayerInfoAddPlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, addPlayerEnumConstant, entityPlayerArr); + + Object playerConnection = ReflectionUtil.getObject(entityPlayer, "playerConnection"); + Class packetClass = ReflectionUtil.getNmsClass("Packet"); + + //Send the two Packets + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutPlayerInfoRemovePlayer }); + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutPlayerInfoAddPlayer }); + } + + private void applySkin1_17(Player player, String skinValue, String skinSignature) throws Exception { + Class craftPlayerClass = ReflectionUtil.getBukkitClass("entity.CraftPlayer"); + Object entityPlayer = ReflectionUtil.invokeMethod(craftPlayerClass, player, "getHandle"); + + Class entityHumanClass = ReflectionUtil.getMinecraftClass("world.entity.player.EntityHuman"); + + Object gameProfile = ReflectionUtil.invokeMethod(entityHumanClass, entityPlayer, "getProfile"); + Object propertyMap = ReflectionUtil.invokeMethod(gameProfile, "getProperties"); + + //Check if the PropertyMap contains the 'textures' property + //If so remove it + //The containsKey method is in the ForwardingMultimap class, which PropertyMap extends + Class forwardingMultimapClass = com.google.common.collect.ForwardingMultimap.class; + Boolean containsKeyTextures = (Boolean) ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "containsKey", new Class[] { Object.class }, new Object[] { "textures" }); + if(containsKeyTextures) { + Object textures = ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "get", new Class[] { Object.class }, new Object[] { "textures" }); + Object texturesIter = ReflectionUtil.invokeMethod(Collection.class, textures, "iterator"); + Object iterNext = ReflectionUtil.invokeMethod(texturesIter, "next"); + + ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "remove", new Class[] { Object.class, Object.class }, new Object[] { "textures", iterNext }); + } + + //Create a new 'textures' Property with the new skinValue and skinSignature + //and put it in the PropertyMap + Class propertyClass = Class.forName("com.mojang.authlib.properties.Property"); + Object newProperty = ReflectionUtil.invokeConstructor(propertyClass, "textures", skinValue, skinSignature); + ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "put", new Class[] { Object.class, Object.class }, new Object[] { "textures", newProperty }); + + Class packetPlayOutPlayerInfoClass = ReflectionUtil.getMinecraftClass("network.protocol.game.PacketPlayOutPlayerInfo"); + Object removePlayerEnumConstant = ReflectionUtil.getEnum(packetPlayOutPlayerInfoClass, "EnumPlayerInfoAction", "REMOVE_PLAYER"); + Object addPlayerEnumConstant = ReflectionUtil.getEnum(packetPlayOutPlayerInfoClass, "EnumPlayerInfoAction", "ADD_PLAYER"); + + //Create an Array of EntityPlayer with size = 1 and add our player to it + Object entityPlayerArr = Array.newInstance(entityPlayer.getClass(), 1); + Array.set(entityPlayerArr, 0, entityPlayer); + + Class enumPlayerInfoActionClass = ReflectionUtil.getMinecraftClass("network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); + + Object packetPlayOutPlayerInfoRemovePlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, + new Class[] { enumPlayerInfoActionClass, entityPlayerArr.getClass() }, + new Object[] { removePlayerEnumConstant, entityPlayerArr }); + + Object packetPlayOutPlayerInfoAddPlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, + new Class[] { enumPlayerInfoActionClass, entityPlayerArr.getClass() }, + new Object[] { removePlayerEnumConstant, entityPlayerArr }); + + Object playerConnection = ReflectionUtil.getObject(entityPlayer, "b"); + Class packetClass = ReflectionUtil.getMinecraftClass("network.protocol.Packet"); + + //Send the two Packets + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutPlayerInfoRemovePlayer }); + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutPlayerInfoAddPlayer }); + } + + private void applySkin1_18(Player player, String skinValue, String skinSignature) throws Exception { + Class craftPlayerClass = ReflectionUtil.getBukkitClass("entity.CraftPlayer"); + Object entityPlayer = ReflectionUtil.invokeMethod(craftPlayerClass, player, "getHandle"); + + Class entityHumanClass = ReflectionUtil.getMinecraftClass("world.entity.player.EntityHuman"); + Object gameProfile = ReflectionUtil.invokeMethod(entityHumanClass, entityPlayer, "fp"); + Object propertyMap = ReflectionUtil.invokeMethod(gameProfile, "getProperties"); + + //Check if the PropertyMap contains the 'textures' property + //If so remove it + //The containsKey method is in the ForwardingMultimap class, which PropertyMap extends + Class forwardingMultimapClass = com.google.common.collect.ForwardingMultimap.class; + Boolean containsKeyTextures = (Boolean) ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "containsKey", new Class[] { Object.class }, new Object[] { "textures" }); + if(containsKeyTextures) { + Object textures = ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "get", new Class[] { Object.class }, new Object[] { "textures" }); + Object texturesIter = ReflectionUtil.invokeMethod(Collection.class, textures, "iterator"); + Object iterNext = ReflectionUtil.invokeMethod(texturesIter, "next"); + + ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "remove", new Class[] { Object.class, Object.class }, new Object[] { "textures", iterNext }); + } + + //Create a new 'textures' Property with the new skinValue and skinSignature + //and put it in the PropertyMap + Class propertyClass = Class.forName("com.mojang.authlib.properties.Property"); + Object newProperty = ReflectionUtil.invokeConstructor(propertyClass, "textures", skinValue, skinSignature); + ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "put", new Class[] { Object.class, Object.class }, new Object[] { "textures", newProperty }); + + Class packetPlayOutPlayerInfoClass = ReflectionUtil.getMinecraftClass("network.protocol.game.PacketPlayOutPlayerInfo"); + Object removePlayerEnumConstant = ReflectionUtil.getEnum(packetPlayOutPlayerInfoClass, "EnumPlayerInfoAction", "REMOVE_PLAYER"); + Object addPlayerEnumConstant = ReflectionUtil.getEnum(packetPlayOutPlayerInfoClass, "EnumPlayerInfoAction", "ADD_PLAYER"); + + //Create an Array of EntityPlayer with size = 1 and add our player to it + Object entityPlayerArr = Array.newInstance(entityPlayer.getClass(), 1); + Array.set(entityPlayerArr, 0, entityPlayer); + + Class enumPlayerInfoActionClass = ReflectionUtil.getMinecraftClass("network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); + + Object packetPlayOutPlayerInfoRemovePlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, + new Class[] { enumPlayerInfoActionClass, entityPlayerArr.getClass() }, + new Object[] { removePlayerEnumConstant, entityPlayerArr }); + + Object packetPlayOutPlayerInfoAddPlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, + new Class[] { enumPlayerInfoActionClass, entityPlayerArr.getClass() }, + new Object[] { removePlayerEnumConstant, entityPlayerArr }); + + Object playerConnection = ReflectionUtil.getObject(entityPlayer, "b"); + Class packetClass = ReflectionUtil.getMinecraftClass("network.protocol.Packet"); + + //Send the two Packets + ReflectionUtil.invokeMethod(playerConnection, "a", new Class[] { packetClass }, new Object[] { packetPlayOutPlayerInfoRemovePlayer }); + ReflectionUtil.invokeMethod(playerConnection, "a", new Class[] { packetClass }, new Object[] { packetPlayOutPlayerInfoAddPlayer }); + } + @SuppressWarnings("deprecation") private void applySkin(Player player, String skinValue, String skinSignature) { new BukkitRunnable() { @@ -123,95 +277,17 @@ private void applySkin(Player player, String skinValue, String skinSignature) { @Override public void run() { try { - Class craftPlayerClass = ReflectionUtil.getBukkitClass("entity.CraftPlayer"); - Object entityPlayer = ReflectionUtil.invokeMethod(craftPlayerClass, player, "getHandle"); - - //Get the GameProfile and the PropertyMap inside the Profile - Class entityHumanClass; - if(ReflectionUtil.isUseNewSpigotPackaging()) { - entityHumanClass = ReflectionUtil.getMinecraftClass("world.entity.player.EntityHuman"); - } else { - entityHumanClass = ReflectionUtil.getNmsClass("EntityHuman"); - } - - Object gameProfile = ReflectionUtil.invokeMethod(entityHumanClass, entityPlayer, "getProfile"); - Object propertyMap = ReflectionUtil.invokeMethod(gameProfile, "getProperties"); - - //Check if the PropertyMap contains the 'textures' property - //If so remove it - //The containsKey method is in the ForwardingMultimap class, which PropertyMap extends - Class forwardingMultimapClass = com.google.common.collect.ForwardingMultimap.class; - Boolean containsKeyTextures = (Boolean) ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "containsKey", new Class[] { Object.class }, new Object[] { "textures" }); - if(containsKeyTextures) { - Object textures = ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "get", new Class[] { Object.class }, new Object[] { "textures" }); - Object texturesIter = ReflectionUtil.invokeMethod(Collection.class, textures, "iterator"); - Object iterNext = ReflectionUtil.invokeMethod(texturesIter, "next"); - - ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "remove", new Class[] { Object.class, Object.class }, new Object[] { "textures", iterNext }); - } - - //Create a new 'textures' Property with the new skinValue and skinSignature - //and put it in the PropertyMap - Class propertyClass = Class.forName("com.mojang.authlib.properties.Property"); - Object newProperty = ReflectionUtil.invokeConstructor(propertyClass, "textures", skinValue, skinSignature); - ReflectionUtil.invokeMethod(forwardingMultimapClass, propertyMap, "put", new Class[] { Object.class, Object.class }, new Object[] { "textures", newProperty }); - - //Get the Enum constants REMOVE_PLAYER and ADD_PLAYER - Class packetPlayOutPlayerInfoClass; - if(ReflectionUtil.isUseNewSpigotPackaging()) { - packetPlayOutPlayerInfoClass = ReflectionUtil.getMinecraftClass("network.protocol.game.PacketPlayOutPlayerInfo"); - } else { - packetPlayOutPlayerInfoClass = ReflectionUtil.getNmsClass("PacketPlayOutPlayerInfo"); + if(!ReflectionUtil.isUseNewSpigotPackaging()) { + applySkin1_16(player, skinValue, skinSignature); + return; } - Object removePlayerEnumConstant = ReflectionUtil.getEnum(packetPlayOutPlayerInfoClass, "EnumPlayerInfoAction", "REMOVE_PLAYER"); - Object addPlayerEnumConstant = ReflectionUtil.getEnum(packetPlayOutPlayerInfoClass, "EnumPlayerInfoAction", "ADD_PLAYER"); - - //Create an Array of EntityPlayer with size = 1 and add our player to it - Object entityPlayerArr = Array.newInstance(entityPlayer.getClass(), 1); - Array.set(entityPlayerArr, 0, entityPlayer); - - //Create two PacketPlayOutPlayerInfo packets, one for removing the player and one for re-adding the player - Object packetPlayOutPlayerInfoRemovePlayer; - if(ReflectionUtil.isUseNewSpigotPackaging()) { - Class enumPlayerInfoActionClass = ReflectionUtil.getMinecraftClass("network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); - - packetPlayOutPlayerInfoRemovePlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, - new Class[] { enumPlayerInfoActionClass, entityPlayerArr.getClass() }, - new Object[] { removePlayerEnumConstant, entityPlayerArr }); - } else { - packetPlayOutPlayerInfoRemovePlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, removePlayerEnumConstant, entityPlayerArr); - } - - Object packetPlayOutPlayerInfoAddPlayer; - if(ReflectionUtil.isUseNewSpigotPackaging()) { - Class enumPlayerInfoActionClass = ReflectionUtil.getMinecraftClass("network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); - packetPlayOutPlayerInfoAddPlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, - new Class[] { enumPlayerInfoActionClass, entityPlayerArr.getClass() }, - new Object[] { removePlayerEnumConstant, entityPlayerArr } - ); - } else { - packetPlayOutPlayerInfoAddPlayer = ReflectionUtil.invokeConstructor(packetPlayOutPlayerInfoClass, addPlayerEnumConstant, entityPlayerArr); - } - - //Get the Player's connection and the generic Packet class - Object playerConnection; - if(ReflectionUtil.isUseNewSpigotPackaging()) { - playerConnection = ReflectionUtil.getObject(entityPlayer, "b"); - } else { - playerConnection = ReflectionUtil.getObject(entityPlayer, "playerConnection"); - } - - Class packetClass; - if(ReflectionUtil.isUseNewSpigotPackaging()) { - packetClass = ReflectionUtil.getMinecraftClass("network.protocol.Packet"); - } else { - packetClass = ReflectionUtil.getNmsClass("Packet"); + switch(ReflectionUtil.getMajorVersion()) { + case 17: applySkin1_17(player, skinValue, skinSignature); break; + default: + applySkin1_18(player, skinValue, skinSignature); break; } - //Send the two Packets - ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutPlayerInfoRemovePlayer }); - ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutPlayerInfoAddPlayer }); } catch(Exception e) { e.printStackTrace(); } @@ -256,9 +332,16 @@ public void run() { } else { playerIntManager = ReflectionUtil.getObject(entityPlayer, "playerInteractManager"); } - - Enum enumGamemode = (Enum) ReflectionUtil.invokeMethod(playerIntManager, "getGameMode"); - int gamemodeId = (int) ReflectionUtil.invokeMethod(enumGamemode, "getId"); + + Enum enumGamemode; + int gamemodeId; + if(ReflectionUtil.getMajorVersion() >= 18) { + enumGamemode = (Enum) ReflectionUtil.invokeMethod(playerIntManager, "b"); + gamemodeId = (int) ReflectionUtil.invokeMethod(enumGamemode, "a"); + } else { + enumGamemode = (Enum) ReflectionUtil.invokeMethod(playerIntManager, "getGameMode"); + gamemodeId = (int) ReflectionUtil.invokeMethod(enumGamemode, "getId"); + } //Get the World's seed, and hash it with sha256 Object seed = ReflectionUtil.invokeMethod(playerLocation.getWorld(), "getSeed"); @@ -267,7 +350,12 @@ public void run() { //Get the EnumGamemode value from the gamemode ID. //We can't use ReflectionUtil to invoke the method because that convert //the primitive int to it's wrapper Integer. - Method getGamemodeByIdMethod = ReflectionUtil.getMethod(enumGamemode.getClass(), "getById", int.class); + Method getGamemodeByIdMethod; + if(ReflectionUtil.getMajorVersion() >= 18) { + getGamemodeByIdMethod = ReflectionUtil.getMethod(enumGamemode.getClass(), "a", int.class); + } else { + getGamemodeByIdMethod = ReflectionUtil.getMethod(enumGamemode.getClass(), "getById", int.class); + } Object gamemodeEnumConst = getGamemodeByIdMethod.invoke(null, gamemodeId); //PacketPlayOutRespawn Class @@ -277,9 +365,8 @@ public void run() { } else { playPacketOutRespawnClass = ReflectionUtil.getNmsClass("PacketPlayOutRespawn"); } - - //Instantiate the PacketPlayOutRespawn packet, + //Instantiate the PacketPlayOutRespawn packet, //Different Minecraft versions have slightly different constructors, so we have multiple Object packetPlayOutRespawn; try { @@ -288,7 +375,7 @@ public void run() { //TypeKey and DimensionKey Object typeKey = ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "getTypeKey"); Object dimensionKey = ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "getDimensionKey"); - + //Instantiate the Packet packetPlayOutRespawn = ReflectionUtil.invokeConstructor(playPacketOutRespawnClass, new Class[] { @@ -318,9 +405,14 @@ public void run() { } catch(Exception ignored) { // 1.16.2+ - // DimensionManager and DimensionKey - Object dimensionManager = ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "getDimensionManager"); - Object dimensionKey = ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "getDimensionKey"); + Object dimensionManager, dimensionKey; + if(ReflectionUtil.getMajorVersion() >= 18) { + dimensionManager = ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "q_"); + dimensionKey = ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "aa"); + } else { + dimensionManager = ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "getDimensionManager"); + dimensionKey = ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "getDimensionKey"); + } /* PacketPlayOutRespawn: * Mojang's variable names to their 'I know what this is'-name @@ -333,7 +425,7 @@ public void run() { * g: (boolean) isFlat * h: (boolean) keepAllPlayerData * */ - packetPlayOutRespawn = ReflectionUtil.invokeConstructor(playPacketOutRespawnClass, + packetPlayOutRespawn = ReflectionUtil.invokeConstructor(playPacketOutRespawnClass, new Class[] { dimensionManager.getClass(), dimensionKey.getClass(), @@ -351,10 +443,14 @@ public void run() { gamemodeEnumConst, //isDebugWorld is in the nms class World, which WorldServer extends - ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "isDebugWorld"), + (ReflectionUtil.getMajorVersion() >= 18) ? + ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "ad") + : ReflectionUtil.invokeMethod(worldServer.getClass().getSuperclass(), worldServer, "isDebugWorld"), //isFlatWorld is in the nms class WorldServer for some reason (I expected it in the nms class World) - ReflectionUtil.invokeMethod(worldServer, "isFlatWorld"), + (ReflectionUtil.getMajorVersion() >= 18) ? + ReflectionUtil.invokeMethod(worldServer, "D") + : ReflectionUtil.invokeMethod(worldServer, "isFlatWorld"), true }); } @@ -447,25 +543,40 @@ public void run() { packetClass = ReflectionUtil.getMinecraftClass("network.protocol.Packet"); } else { packetClass = ReflectionUtil.getNmsClass("Packet"); - } - //Send both the PacketPlayOutPlayerInfo packets - ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutRemovePlayer }); - ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutAddPlayer }); - - //Send the PacketPlayOutRespawn packet - ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutRespawn }); - - //Update the Player's abilities - ReflectionUtil.invokeMethod(entityPlayer, "updateAbilities"); - - //Send both the PacketPlayOutPosition and PacketPlayOutHeldItem packets - ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] {packetPlayOutPosition}); - ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] {packetPlayOutHeldItemSlot}); - - //Update the Player's healthbar and inventory - ReflectionUtil.invokeMethod(player, "updateScaledHealth"); - ReflectionUtil.invokeMethod(player, "updateInventory"); - ReflectionUtil.invokeMethod(entityPlayer, "triggerHealthUpdate"); + } + + if(ReflectionUtil.getMajorVersion() >= 18) { + ReflectionUtil.invokeMethod(playerConnection, "a", new Class[] { packetClass }, new Object[] { packetPlayOutRemovePlayer }); // Send packet + ReflectionUtil.invokeMethod(playerConnection, "a", new Class[] { packetClass }, new Object[] { packetPlayOutAddPlayer }); + ReflectionUtil.invokeMethod(playerConnection, "a", new Class[] { packetClass }, new Object[] { packetPlayOutRespawn }); + ReflectionUtil.invokeMethod(entityPlayer, "w"); // onUpdateAbilities + ReflectionUtil.invokeMethod(playerConnection, "a", new Class[] { packetClass }, new Object[] {packetPlayOutPosition}); + ReflectionUtil.invokeMethod(playerConnection, "a", new Class[] { packetClass }, new Object[] {packetPlayOutHeldItemSlot}); + } else { + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[]{packetClass}, new Object[]{packetPlayOutRemovePlayer}); + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[]{packetClass}, new Object[]{packetPlayOutAddPlayer}); + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] { packetPlayOutRespawn }); + ReflectionUtil.invokeMethod(entityPlayer, "updateAbilities"); + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] {packetPlayOutPosition}); + ReflectionUtil.invokeMethod(playerConnection, "sendPacket", new Class[] { packetClass }, new Object[] {packetPlayOutHeldItemSlot}); + } + + //Update the Player's healthbar and inventory + ReflectionUtil.invokeMethod(player, "updateScaledHealth"); + ReflectionUtil.invokeMethod(player, "updateInventory"); + + if(ReflectionUtil.getMajorVersion() >= 18) { + Class packetPlayOutUpdateHealthClass = ReflectionUtil.getMinecraftClass("network.protocol.game.PacketPlayOutUpdateHealth"); + Object packetPlayOutUpdateHealth = ReflectionUtil.invokeConstructor( + packetPlayOutUpdateHealthClass, + new Class[] { float.class, int.class, float.class }, + new Object[] { (float) player.getHealth() ,player.getFoodLevel(), player.getSaturation() } + ); + + ReflectionUtil.invokeMethod(playerConnection, "a", new Class[] { packetClass}, new Object[] { packetPlayOutUpdateHealth }); + } else { + ReflectionUtil.invokeMethod(entityPlayer, "triggerHealthUpdate"); + } //If the Player is OP, we have to toggle it off and back on really quickly for it to work if(player.isOp()) { diff --git a/src/main/java/dev/array21/skinfixer/SkinFixer.java b/src/main/java/dev/array21/skinfixer/SkinFixer.java index 7eb9fc4..42ec1aa 100644 --- a/src/main/java/dev/array21/skinfixer/SkinFixer.java +++ b/src/main/java/dev/array21/skinfixer/SkinFixer.java @@ -7,8 +7,6 @@ import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; -import dev.array21.pluginstatlib.PluginStat; -import dev.array21.pluginstatlib.PluginStat.PluginStatBuilder; import dev.array21.skinfixer.annotations.Nullable; import dev.array21.skinfixer.commands.CommandHandler; import dev.array21.skinfixer.config.ConfigHandler; @@ -26,7 +24,6 @@ public class SkinFixer extends JavaPlugin { public static String PLUGIN_VERSION; public static final Logger LOGGER = LogManager.getLogger(SkinFixer.class); - private HashMap skinUuidCodes = new HashMap<>(); private HashMap skinUrlCodes = new HashMap<>(); @@ -44,7 +41,7 @@ public void onEnable() { //Read the configuration this.configHandler = new ConfigHandler(this); ConfigManifest configManifest = configHandler.read(); - + if(configManifest.updateCheck) { new Thread(new Runnable() { @Override @@ -54,7 +51,7 @@ public void run() { } }, "SkinFixer UpdateChecker Thread").start(); } - + this.libWrapper = new LibWrapper(this); this.libWrapper.init(); @@ -66,16 +63,6 @@ public void run() { langHandler.loadLang("en"); } - if(configManifest.sendMetrics) { - PluginStat stat = PluginStatBuilder.createDefault() - .setLogErrFn(SkinFixer::logWarn) - .setSetUuidFn(configHandler::setStatUuid) - .setUuid(configManifest.metricsUuid) - .build(); - - stat.start(); - } - CommandHandler commandHandler = new CommandHandler(this); this.getCommand("skin").setExecutor(commandHandler); this.getCommand("skin").setTabCompleter(commandHandler); diff --git a/src/main/java/dev/array21/skinfixer/apis/SkinFixerApi.java b/src/main/java/dev/array21/skinfixer/apis/SkinFixerApi.java index b97ba08..5319a23 100644 --- a/src/main/java/dev/array21/skinfixer/apis/SkinFixerApi.java +++ b/src/main/java/dev/array21/skinfixer/apis/SkinFixerApi.java @@ -11,9 +11,9 @@ import dev.array21.skinfixer.apis.gson.GetUuidResponse; import dev.array21.skinfixer.util.Triple; import dev.array21.skinfixer.util.Utils; -import nl.thedutchmc.httplib.Http; -import nl.thedutchmc.httplib.Http.RequestMethod; -import nl.thedutchmc.httplib.Http.ResponseObject; +import dev.array21.httplib.Http; +import dev.array21.httplib.Http.RequestMethod; +import dev.array21.httplib.Http.ResponseObject; public class SkinFixerApi { diff --git a/src/main/java/dev/array21/skinfixer/storage/LibWrapper.java b/src/main/java/dev/array21/skinfixer/storage/LibWrapper.java index fdbac96..18a96a2 100644 --- a/src/main/java/dev/array21/skinfixer/storage/LibWrapper.java +++ b/src/main/java/dev/array21/skinfixer/storage/LibWrapper.java @@ -26,7 +26,7 @@ public class LibWrapper { String osName = System.getProperty("os.name").toLowerCase(); if(osName.contains("linux")) { switch(System.getProperty("os.arch")) { - case "amd64": libName = "/x86_64/linux/libskinfixer-focal.so"; break; + case "amd64": libName = "/x86_64/linux/libskinfixer.so"; break; case "arm": libName = "/armhf/linux/libskinfixer.so"; break; case "aarch64": libName = "/aarch64/linux/libskinfixer.so"; break; default: @@ -65,29 +65,8 @@ public class LibWrapper { try { System.load(libTmpFile.getAbsolutePath()); } catch(UnsatisfiedLinkError e) { - if(osName.contains("linux")) { - SkinFixer.logWarn("Failed to load libskinfixer Focal. Trying Bionic."); - - String loadBionicError = tryLoadAmd64Linux("bionic"); - if(loadBionicError != null) { - SkinFixer.logWarn(loadBionicError); - SkinFixer.logWarn("Failed to load libskinfixer Bionic. Trying Xenial."); - - String loadXenialError = tryLoadAmd64Linux("xenial"); - if(loadXenialError != null) { - SkinFixer.logWarn(loadXenialError); - SkinFixer.logWarn("Failed to load libskinfixer Focal."); - printLibDebugHelp(); - } - } - } else { - SkinFixer.logWarn("Failed to load library: " + e.getMessage()); - libTmpFile.delete(); - tmpDir.delete(); - - printLibDebugHelp(); - break saveLib; - } + SkinFixer.logWarn("Failed to load libskinfixer. Please open an issue at https://github.com/TheDutchMC/SkinFixer/issues and include your OS, architecture and SkinFixer version. Thanks!"); + SkinFixer.logWarn(Utils.getStackTrace(e)); } SkinFixer.logInfo("libskinfixer loaded."); @@ -95,37 +74,6 @@ public class LibWrapper { } } - private static String tryLoadAmd64Linux(String distro) { - String name = String.format("/x86_64/linux/libskinfixer-%s.so", distro); - Pair pairedFile = saveLib(name); - if(pairedFile == null) { - return String.format("Failed to save library libskinfixer-%.so", distro); - } - - File tmpFolder = pairedFile.getA(); - File libTmpFile = pairedFile.getB(); - - try { - System.load(libTmpFile.getAbsolutePath()); - } catch(UnsatisfiedLinkError e) { - libTmpFile.delete(); - tmpFolder.delete(); - return String.format("Failed to load library libskinfixer-%.so: %s", distro, Utils.getStackTrace(e)); - } - - return null; - } - - private static void printLibDebugHelp() { - SkinFixer.logWarn("Check that all required dependencies are installed."); - SkinFixer.logWarn("You should make sure that you are using GLIBC >=2.23."); - SkinFixer.logWarn("If you are using GLIBC =< 2.23, make sure that you have libssl 1.0.0 AND libcrypto 1.0.0 installed."); - SkinFixer.logWarn("If you are using GLIBC >= 2.31, make sure that you have OpenSSL (libssl 1.1 AND libcrypto 1.1)installed."); - SkinFixer.logWarn("For more help you can join Dutchy76's Discord: https://discord.com/invite/xE3FcGj"); - SkinFixer.logWarn("Alternatively, you can open an issue on GitHub: https://github.com/TheDutchMC/SkinFixer/issues/new/choose"); - SkinFixer.logWarn("In either case, please include your Operating System, OS version, architecture, Minecraft version and the version of SkinFixer you are using"); - } - @Nullable private static Pair saveLib(String libName) { URL libUrl = LibWrapper.class.getResource(libName); @@ -134,6 +82,7 @@ private static Pair saveLib(String libName) { tmpDir = Files.createTempDirectory("libskinfixer").toFile(); } catch (IOException e) { SkinFixer.logWarn("Failed to create temporary directory: " + e); + SkinFixer.logWarn(Utils.getStackTrace(e)); return null; } @@ -145,15 +94,15 @@ private static Pair saveLib(String libName) { Files.copy(is, libTmpFile.toPath()); } catch(IOException e) { tmpDir.delete(); - SkinFixer.logWarn("Failed to save dynamic library as temporay file: " + e); + SkinFixer.logWarn(Utils.getStackTrace(e)); return null; } libTmpFile.deleteOnExit(); tmpDir.deleteOnExit(); - return new Pair(tmpDir, libTmpFile); + return new Pair<>(tmpDir, libTmpFile); } private SkinFixer plugin; diff --git a/src/main/java/dev/array21/skinfixer/updatechecker/UpdateChecker.java b/src/main/java/dev/array21/skinfixer/updatechecker/UpdateChecker.java index f9ee028..457b6c1 100644 --- a/src/main/java/dev/array21/skinfixer/updatechecker/UpdateChecker.java +++ b/src/main/java/dev/array21/skinfixer/updatechecker/UpdateChecker.java @@ -8,9 +8,9 @@ import dev.array21.skinfixer.SkinFixer; import dev.array21.skinfixer.util.Pair; -import nl.thedutchmc.httplib.Http; -import nl.thedutchmc.httplib.Http.RequestMethod; -import nl.thedutchmc.httplib.Http.ResponseObject; +import dev.array21.httplib.Http; +import dev.array21.httplib.Http.RequestMethod; +import dev.array21.httplib.Http.ResponseObject; public class UpdateChecker { diff --git a/src/main/java/dev/array21/skinfixer/util/ReflectionUtil.java b/src/main/java/dev/array21/skinfixer/util/ReflectionUtil.java deleted file mode 100644 index 4c9c467..0000000 --- a/src/main/java/dev/array21/skinfixer/util/ReflectionUtil.java +++ /dev/null @@ -1,320 +0,0 @@ -package dev.array21.skinfixer.util; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import org.bukkit.Bukkit; - -import net.dv8tion.jda.annotations.ForRemoval; - -public class ReflectionUtil { - - public static String SERVER_VERSION; - - static { - //Load the Bukit class - try { - Class.forName("org.bukkit.Bukkit"); - } catch (ClassNotFoundException ignored) {} - - //Get the version String - SERVER_VERSION = Bukkit.getServer().getClass().getPackage().getName().substring(Bukkit.getServer().getClass().getPackage().getName().lastIndexOf('.') + 1); - } - - public static boolean isUseNewSpigotPackaging() { - String major = SERVER_VERSION.split("_")[1]; - return Integer.valueOf(major) >= 17; - } - - /** - * Get a Class from the org.bukkit.craftbukkit.SERVER_VERSION. package - * @param className The name of the class - * @return Returns the Class - * @throws ClassNotFoundException Thrown when the Class was not found - */ - public static Class getBukkitClass(String className) throws ClassNotFoundException { - return Class.forName("org.bukkit.craftbukkit." + SERVER_VERSION + "." + className); - } - - /** - * MC:1.16 and lower only - * - * Get a Class from the net.minecraft.server.SERVER_VERSION. package - * @param className The name of the class - * @return Returns the Class - * @throws ClassNotFoundException Thrown when the Class was not found - */ - @Deprecated - @ForRemoval - public static Class getNmsClass(String className) throws ClassNotFoundException { - return Class.forName("net.minecraft.server." + SERVER_VERSION + "." + className); - } - - public static Class getMinecraftClass(String className) throws ClassNotFoundException { - return Class.forName("net.minecraft." + className); - } - - /** - * Get the Constructor of a Class - * @param clazz The Class in which the Constructor is defined - * @param args Arguments taken by the Constructor - * @return Returns the Constructor - * @throws NoSuchMethodException Thrown when no Constructor in the Class was found with the provided combination of arguments - */ - public static Constructor getConstructor(Class clazz, Class... args) throws NoSuchMethodException { - Constructor con = clazz.getDeclaredConstructor(args); - con.setAccessible(true); - - return con; - } - - /** - * Get an Enum from an Enum constant - * @param clazz The Class in which the Enum is defined - * @param constant The name of the Enum Constant - * @return Returns the Enum - * @throws ClassNotFoundException - */ - public static Enum getEnum(Class clazz, String constant) throws ClassNotFoundException { - Class c = Class.forName(clazz.getName()); - Enum[] constants = (Enum[]) c.getEnumConstants(); - - for(Enum e : constants) { - if(e.name().equalsIgnoreCase(constant)) { - return e; - } - } - - return null; - } - - /** - * Get an Enum constant by it's name and constant - * @param clazz The Class in which the Enum is defined - * @param enumname The name of the Enum - * @param constant The name of the Constant - * @return Returns the Enum - * @throws ClassNotFoundException - */ - public static Enum getEnum(Class clazz, String enumname, String constant) throws ClassNotFoundException { - Class c = Class.forName(clazz.getName() + "$" + enumname); - Enum[] econstants = (Enum[]) c.getEnumConstants(); - - for (Enum e : econstants) { - if (e.name().equalsIgnoreCase(constant)) { - return e; - } - } - - return null; - } - - /** - * Get a Field - * @param clazz The Class in which the Field is defined - * @param fieldName The name of the Field - * @return Returns the Field - * @throws NoSuchFieldException Thrown when the Field was not present in the Class - */ - public static Field getField(Class clazz, String fieldName) throws NoSuchFieldException { - Field f = clazz.getDeclaredField(fieldName); - f.setAccessible(true); - return f; - } - - /** - * Get a Method - * @param clazz The Class in which the Method is defined - * @param methodName The name of the method - * @param args The argument types the method takes - * @return Returns the Method - * @throws NoSuchMethodException - */ - public static Method getMethod(Class clazz, String methodName, Class... args) throws NoSuchMethodException { - Method m = clazz.getDeclaredMethod(methodName, args); - m.setAccessible(true); - return m; - } - - /** - * Invoke a Method which takes no arguments. The Class in which the Method is defined is derived from the provided Object - * @param obj The object to invoke the method on - * @param methodName The name of the Method - * @return Returns the result of the method, can be null if the method returns void - * @throws NoSuchMethodException - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - */ - public static Object invokeMethod(Object obj, String methodName) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - Method m = getMethod(obj.getClass(), methodName); - return m.invoke(obj); - } - - /** - * Invoke a Method where the argument types are derived from the provided arguments. The Class in which the Method is defined is derived from the provided Object - * @param obj The object to invoke the method on - * @param methodName The name of the Method - * @param args The arguments to pass to the Method - * @return Returns the result of the method, can be null if the method returns void - * @throws NoSuchMethodException - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - */ - public static Object invokeMethod(Object obj, String methodName, Object[] args) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - return invokeMethod(obj.getClass(), obj, methodName, args); - } - - /** - * Invoke a Method where the argument types are explicitly given (Helpful when working with primitives). The Class in which the Method is defined is derived from the provided Object. - * @param obj The Object to invoke the method on - * @param methodName The name of the Method - * @param argTypes The types of arguments as a Class array - * @param args The arguments as an object array - * @return Returns the result of the method, can be null if the method returns void - * @throws NoSuchMethodException - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - */ - public static Object invokeMethod(Object obj, String methodName, Class[] argTypes, Object[] args) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - Method m = getMethod(obj.getClass(), methodName, argTypes); - return m.invoke(obj, args); - } - - /** - * Invoke a Method where the Class where to find the method is explicitly given (Helpful if the method is located in a superclass). The argument types are derived from the provided arguments - * @param clazz The Class where the method is located - * @param obj The Object to invoke the method on - * @param methodName The name of the method - * @param args The arguments to be passed to the method - * @return Returns the result of the method, can be null if the method returns void - * @throws NoSuchMethodException - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - */ - public static Object invokeMethod(Class clazz, Object obj, String methodName, Object... args) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - Class[] argTypes = new Class[args.length]; - for(int i = 0; i < args.length; i++) { - argTypes[i] = args[i].getClass(); - } - - Method m = getMethod(clazz, methodName, argTypes); - - return m.invoke(obj, args); - } - - /** - * Invoke a Method where the Class where the Method is defined is explicitly given, and the argument types are explicitly given - * @param clazz The Class in which the Method is located - * @param obj The Object on which to invoke the Method - * @param methodName The name of the Method - * @param argTypes Argument types - * @param args Arguments to pass to the method - * @return Returns the result of the method, can be null if the method returns void - * @throws NoSuchMethodException - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - */ - public static Object invokeMethod(Class clazz, Object obj, String methodName, Class[] argTypes, Object[] args) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - Method m = getMethod(clazz, methodName, argTypes); - return m.invoke(obj, args); - } - - /** - * Get the value of a Field, where the Class in which the field is defined is derived from the provided Object - * @param obj The object in which the field is located, and from which to get the value - * @param name The name of the Field to get the value from - * @return Returns the value of the Field - * @throws NoSuchFieldException - * @throws IllegalArgumentException - * @throws IllegalAccessException - */ - public static Object getObject(Object obj, String name) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { - Field f = getField(obj.getClass(), name); - return f.get(obj); - } - - /** - * Get the value of a Field, where the Class in which the Field is defined is explicitly given. (Helpful when the Field is in a superclass) - * @param obj The Object to get the value from - * @param clazz The Class in which the Field is defined - * @param name The name of the Field - * @return Returns the value of the Field - * @throws NoSuchFieldException - * @throws IllegalArgumentException - * @throws IllegalAccessException - */ - public static Object getObject(Object obj, Class clazz, String name) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { - Field f = getField(clazz, name); - return f.get(obj); - } - - /** - * Invoke a Class' constructor. The argument types are derived from the provided arguments - * @param clazz The Class in which the Constructor is defined - * @param args The arguments to pass to the Constructor - * @return Returns an instance of the provided Class in which the Constructor is located - * @throws NoSuchMethodException - * @throws InstantiationException - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - */ - public static Object invokeConstructor(Class clazz, Object... args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - Class[] argTypes = new Class[args.length]; - for(int i = 0; i < args.length; i++) { - argTypes[i] = args[i].getClass(); - } - - Constructor con = getConstructor(clazz, argTypes); - - return con.newInstance(args); - } - - /** - * Invoke a Class' Constructor, where the argument types are explicitly given (Helpful when working with primitives) - * @param clazz The Class in which the Constructor is defined - * @param argTypes The argument types - * @param args The arguments to pass to the constructor - * @return Returns an instance of the provided Class in which the Constructor is located - * @throws NoSuchMethodException - * @throws InstantiationException - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - */ - public static Object invokeConstructor(Class clazz, Class[] argTypes, Object[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - Constructor con = getConstructor(clazz, argTypes); - return con.newInstance(args); - } - - /** - * For debugging purposes!
- * Print all Methods in a Class with their parameters, will print to stdout - * @param clazz The Class to look in - */ - public static void printMethodsInClassTyped(Class clazz) { - System.out.println("Methods in " + clazz.getName() + ":"); - - for(Method m : clazz.getDeclaredMethods()) { - String print = m.getName() + "("; - for(int i = 0; i < m.getParameterTypes().length; i++) { - print += m.getParameterTypes()[i].getName(); - - if(i != m.getParameterTypes().length -1) { - print += ","; - } - - } - - print += ")"; - System.out.println(print); - } - } -} \ No newline at end of file