Compare commits

..
10 Commits
Author SHA1 Message Date
Dan Mulloy d9c623a254 backwards compat 2022-12-13 20:32:53 -05:00
Dan Mulloy fe0abbf9b3 revert some changes to MinecraftReflection 2022-12-13 20:30:51 -05:00
Dan Mulloy 92b604506f line endings 2022-12-13 20:25:07 -05:00
derklaro 574c34ad5c remove unneeded packet class names 2022-12-08 09:33:29 +01:00
derklaro 115cc83040 resolve conflicts 2022-12-08 09:14:04 +01:00
derklaro a18c418f42 1.19.3 2022-12-08 09:03:45 +01:00
derklaro 5ef24a7471 22w43a 2022-10-27 16:04:17 +02:00
derklaro 475f89ab89 deprecate WrappedServerPing chat preview stuff 2022-10-20 10:10:41 +02:00
derklaro e3d4339659 player info action changes 2022-10-19 20:04:33 +02:00
derklaro e7d54cdabd update to 1.19.3 2022-10-19 16:24:10 +02:00
79 changed files with 1187 additions and 3402 deletions

No files matched your search

+5 -5
View File
@@ -15,14 +15,14 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
cache: 'gradle'
java-version: '17'
cache: 'maven'
- name: Run gradle build lifecycle
run: ./gradlew build shadowJar --no-daemon
- name: Run maven build lifecycle
run: mvn --batch-mode clean test package
- name: Upload plugin file
uses: actions/upload-artifact@v2
with:
name: ProtocolLib
path: build/libs/ProtocolLib.jar
path: target/ProtocolLib.jar
+4 -4
View File
@@ -26,16 +26,16 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
cache: 'gradle'
java-version: '17'
cache: 'maven'
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: 'java'
- name: Run gradle build lifecycle
run: ./gradlew build -x test --no-daemon
- name: Run maven build lifecycle
run: mvn --batch-mode clean test package
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+6 -6
View File
@@ -10,6 +10,8 @@ Critically, different plugins that use this approach may _hook_ into the same cl
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
lead to more subtle bugs.
Currently maintained by dmulloy2 on behalf of [Spigot](https://www.spigotmc.org/).
### Resources
* [Resource Page](https://www.spigotmc.org/resources/protocollib.1997/)
@@ -18,10 +20,8 @@ lead to more subtle bugs.
### Compilation
ProtocolLib is built with [Gradle](https://gradle.org/). If you have it installed, just run
`./gradlew build` in the root project folder. Other gradle targets you may be interested in
include `clean`, `test`, and `shadowJar`. `shadowJar` will create a jar with all dependencies
(ByteBuddy) included.
ProtocolLib is built with [Maven](https://maven.apache.org/). If you have it installed, just run
`mvn package` in the root project folder.
### A new API
@@ -53,7 +53,7 @@ You can also add ProtocolLib as a Maven dependency:
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>5.0.0</version>
<version>4.7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -67,7 +67,7 @@ repositories {
}
dependencies {
compileOnly 'com.comphenix.protocol:ProtocolLib:5.0.0'
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.7.0";
}
```
@@ -3,8 +3,6 @@ package com.comphenix.tinyprotocol;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -179,31 +177,6 @@ public final class Reflection {
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
}
/**
* Retrieves a field with a given type and parameters. This is most useful
* when dealing with Collections.
*
* @param target the target class.
* @param fieldType Type of the field
* @param params Variable length array of type parameters
* @return The field
*
* @throws IllegalArgumentException If the field cannot be found
*/
public static Field getParameterizedField(Class<?> target, Class<?> fieldType, Class<?>... params) {
for (Field field : target.getDeclaredFields()) {
if (field.getType().equals(fieldType)) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params))
return field;
}
}
}
throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params));
}
/**
* Search for the first publicly and privately defined method of the given name and parameter count.
*
@@ -328,23 +301,6 @@ public final class Reflection {
return clazz;
}
/**
* Retrieve a class from its full name with alternatives, without knowing its type on compile time.
* <p>
* This is useful when looking up fields by a NMS or OBC type.
* <p>
*
* @see {@link #getClass()} for more information.
* @param lookupName - the class name with variables.
* @param aliases - alternative names for this class.
* @return The class.
*/
public static Class<Object> getUntypedClass(String lookupName, String... aliases) {
@SuppressWarnings({ "rawtypes", "unchecked" })
Class<Object> clazz = (Class) getClass(lookupName, aliases);
return clazz;
}
/**
* Retrieve a class from its full name.
* <p>
@@ -377,61 +333,6 @@ public final class Reflection {
return getCanonicalClass(expandVariables(lookupName));
}
/**
* Retrieve the first class that matches the full class name.
* <p>
* Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
* <p>
* <table border="1">
* <tr>
* <th>Variable</th>
* <th>Content</th>
* </tr>
* <tr>
* <td>{nms}</td>
* <td>Actual package name of net.minecraft.server.VERSION</td>
* </tr>
* <tr>
* <td>{obc}</td>
* <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
* </tr>
* <tr>
* <td>{version}</td>
* <td>The current Minecraft package VERSION, if any.</td>
* </tr>
* </table>
*
* @param lookupName - the class name with variables.
* @param aliases - alternative names for this class.
* @return Class object.
* @throws RuntimeException If we are unable to find any of the given classes.
*/
public static Class<?> getClass(String lookupName, String... aliases) {
try {
// Try the main class first
return getClass(lookupName);
} catch (RuntimeException e) {
Class<?> success = null;
// Try every alias too
for (String alias : aliases) {
try {
success = getClass(alias);
break;
} catch (RuntimeException e1) {
// e1.printStackTrace();
}
}
if (success != null) {
return success;
} else {
// Hack failed
throw new RuntimeException(String.format("Unable to find %s (%s)", lookupName, String.join(",", aliases)));
}
}
}
/**
* Retrieve a class in the net.minecraft.server.VERSION.* package.
*
@@ -9,7 +9,6 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -45,25 +44,21 @@ import com.mojang.authlib.GameProfile;
public abstract class TinyProtocol {
private static final AtomicInteger ID = new AtomicInteger(0);
// Required Minecraft classes
private static final Class<?> entityPlayerClass = Reflection.getClass("{nms}.EntityPlayer", "net.minecraft.server.level.EntityPlayer");
private static final Class<?> playerConnectionClass = Reflection.getClass("{nms}.PlayerConnection", "net.minecraft.server.network.PlayerConnection");
private static final Class<?> networkManagerClass = Reflection.getClass("{nms}.NetworkManager", "net.minecraft.network.NetworkManager");
// Used in order to lookup a channel
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
private static final FieldAccessor<?> getConnection = Reflection.getField(entityPlayerClass, null, playerConnectionClass);
private static final FieldAccessor<?> getManager = Reflection.getField(playerConnectionClass, null, networkManagerClass);
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManagerClass, Channel.class, 0);
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
// Looking up ServerConnection
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer", "net.minecraft.server.MinecraftServer");
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection", "net.minecraft.server.network.ServerConnection");
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
// Packets we have to intercept
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart");
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
// Speedup channel lookup
@@ -204,22 +199,8 @@ public abstract class TinyProtocol {
Object serverConnection = getServerConnection.get(mcServer);
boolean looking = true;
try {
Field field = Reflection.getParameterizedField(serverConnectionClass, List.class, networkManagerClass);
field.setAccessible(true);
networkManagers = (List<Object>) field.get(serverConnection);
} catch (Exception ex) {
plugin.getLogger().info("Encountered an exception checking list fields" + ex);
MethodInvoker method = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
networkManagers = (List<Object>) method.invoke(null, serverConnection);
}
if (networkManagers == null) {
throw new IllegalArgumentException("Failed to obtain list of network managers");
}
// We need to synchronize against this list
networkManagers = (List<Object>) getNetworkMarkers.invoke(null, serverConnection);
createServerChannelHandler();
// Find the correct list, or implicitly throw an exception
-114
View File
@@ -1,114 +0,0 @@
plugins {
id 'java-library'
id 'maven-publish'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
group = 'com.comphenix.protocol'
version = '5.0.0'
description = 'Provides access to the Minecraft protocol'
def isSnapshot = version.endsWith('-SNAPSHOT')
repositories {
// mavenLocal() // can speed up build, but may fail in CI
mavenCentral()
maven {
url 'https://repo.dmulloy2.net/repository/public/'
}
maven {
url 'https://hub.spigotmc.org/nexus/content/groups/public/'
}
maven {
url 'https://libraries.minecraft.net/'
metadataSources {
mavenPom()
artifact()
ignoreGradleMetadataRedirection()
}
}
}
dependencies {
implementation 'net.bytebuddy:byte-buddy:1.14.3'
compileOnly 'org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT'
compileOnly 'org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT'
compileOnly 'io.netty:netty-all:4.0.23.Final'
compileOnly 'net.kyori:adventure-text-serializer-gson:4.13.0'
compileOnly 'com.googlecode.json-simple:json-simple:1.1.1'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
testImplementation 'org.mockito:mockito-core:4.11.0'
testImplementation 'org.mockito:mockito-inline:4.11.0'
testImplementation 'io.netty:netty-common:4.1.77.Final'
testImplementation 'io.netty:netty-transport:4.1.77.Final'
testImplementation 'org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT'
testImplementation 'net.kyori:adventure-text-serializer-gson:4.13.0'
testImplementation 'net.kyori:adventure-text-serializer-plain:4.13.1'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
withJavadocJar()
withSourcesJar()
}
shadowJar {
dependencies {
include(dependency('net.bytebuddy:byte-buddy:.*'))
}
relocate 'net.bytebuddy', 'com.comphenix.net.bytebuddy'
archiveFileName = 'ProtocolLib.jar'
}
test {
useJUnitPlatform()
}
processResources {
def includeBuild = isSnapshot && System.getenv('BUILD_NUMBER')
def fullVersion = includeBuild
? version + '-' + System.getenv('BUILD_NUMBER')
: version
eachFile { expand(['version': fullVersion]) }
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
afterEvaluate {
artifactId = 'ProtocolLib'
}
}
}
repositories {
maven {
url isSnapshot
? 'https://repo.dmulloy2.net/repository/snapshots/'
: 'https://repo.dmulloy2.net/repository/releases/'
credentials {
username project.nexusUsername
password project.nexusPassword
}
}
}
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
}
-2
View File
@@ -1,2 +0,0 @@
nexusUsername=""
nexusPassword=""
Binary file not shown.
-6
View File
@@ -1,6 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
-244
View File
@@ -1,244 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# 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
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
-92
View File
@@ -1,92 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+379
View File
@@ -0,0 +1,379 @@
<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>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>5.0.0-SNAPSHOT</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>https://github.com/dmulloy2/ProtocolLib</url>
<packaging>jar</packaging>
<inceptionYear>2012</inceptionYear>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
<junit.version>5.9.0</junit.version>
<mockito.version>4.6.1</mockito.version>
<netty.version>4.1.77.Final</netty.version>
<spigot.version>1.19.3-R0.1-SNAPSHOT</spigot.version>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>net.bytebuddy</pattern>
<shadedPattern>com.comphenix.net.bytebuddy</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<excludes>
<exclude>org.spigotmc:spigot</exclude>
<exclude>org.spigotmc:spigot-api</exclude>
<exclude>junit:junit</exclude>
<exclude>io.netty:*</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.fullVersion}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
<configuration>
<trimStackTrace>false</trimStackTrace>
<useFile>false</useFile>
<systemProperties>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemProperties>
<!-- We need this as we use reflection in tests to ensure equality -->
<argLine>--add-opens java.base/java.util=ALL-UNNAMED</argLine>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<failOnError>false</failOnError>
<encoding>ISO-8859-1</encoding>
<doctitle>ProtocolLib JavaDocs</doctitle>
<windowtitle>ProtocolLib JavaDocs</windowtitle>
<bottom>Copyright &#169; {inceptionYear}&#x2013;{currentYear}
Comphenix and dmulloy2. Licensed under the GNU GPL v2.
</bottom>
<additionalOptions>
<additionalOption>-Xdoclint:none</additionalOption>
</additionalOptions>
</configuration>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jenkins</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>
<!--<project.fullVersion>${project.version}</project.fullVersion>-->
</properties>
</profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
</profile>
</profiles>
<scm>
<connection>scm:git:git://github.com/dmulloy2/ProtocolLib.git</connection>
<developerConnection>scm:git:git@github.com:dmulloy2/ProtocolLib.git</developerConnection>
<url>https://github.com/dmulloy2/ProtocolLib</url>
</scm>
<licenses>
<license>
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
<url>https://www.gnu.org/licenses/gpl-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>https://dmulloy2.net/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
</roles>
</developer>
<developer>
<id>aadnk</id>
<name>Kristian S. Stangeland</name>
<email>kr_stang@hotmail.com</email>
<url>https://comphenix.net/</url>
<roles>
<role>former author</role>
</roles>
<timezone>1</timezone>
</developer>
</developers>
<distributionManagement>
<repository>
<id>dmulloy2-releases</id>
<url>https://repo.dmulloy2.net/repository/releases/</url>
</repository>
<snapshotRepository>
<id>dmulloy2-snapshots</id>
<url>https://repo.dmulloy2.net/repository/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- core minecraft dependencies -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Compile with the old version of Netty for backwards compat -->
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/net.kyori/adventure-text-serializer-gson -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
<version>4.11.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.12.12</version>
</dependency>
<!-- Testing dependencies -->
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-common -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>${netty.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-transport -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${netty.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
-5
View File
@@ -1,5 +0,0 @@
/*
* This file was generated by the Gradle 'init' task.
*/
rootProject.name = 'ProtocolLib'
@@ -446,7 +446,7 @@ class CommandPacket extends CommandBase {
{
final String text = arguments.remove().toLowerCase();
switch (text) {
switch (arguments.remove().toLowerCase()) {
case "add":
return SubCommand.ADD;
case "remove":
@@ -41,19 +41,19 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public static class Handshake {
static final Protocol PROTOCOL = Protocol.HANDSHAKING;
private static final Protocol PROTOCOL = Protocol.HANDSHAKING;
/**
* Incoming packets.
* @author Kristian
*/
public static class Client extends PacketTypeEnum {
private static final Sender SENDER = Sender.CLIENT;
private final static Sender SENDER = Sender.CLIENT;
@ForceAsync
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, "SetProtocol", "C00Handshake");
private static final Client INSTANCE = new Client();
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(); }
@@ -71,8 +71,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public static class Server extends PacketTypeEnum {
private static final Sender SENDER = Sender.CLIENT;
private static final Server INSTANCE = new Server();
private final static Sender SENDER = Sender.CLIENT;
private final static Server INSTANCE = new Server();
private Server() { super(); }
public static Server getInstance() {
@@ -93,126 +93,122 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public static class Play {
static final Protocol PROTOCOL = Protocol.PLAY;
private static final Protocol PROTOCOL = Protocol.PLAY;
/**
* Outgoing packets.
* @author Kristian
*/
public static class Server extends PacketTypeEnum {
private static final Sender SENDER = Sender.SERVER;
private final static Sender SENDER = Sender.SERVER;
public static final PacketType BUNDLE = new PacketType(PROTOCOL, SENDER, 0x00, "Delimiter", "BundleDelimiterPacket");
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x01, "SpawnEntity", "SPacketSpawnObject");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x02, "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x03, "NamedEntitySpawn", "SPacketSpawnPlayer");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x04, "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x05, "Statistic", "SPacketStatistics");
public static final PacketType BLOCK_CHANGED_ACK = new PacketType(PROTOCOL, SENDER, 0x06, "BlockChangedAck");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x07, "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x08, "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x09, "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0A, "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0B, "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0C, "ServerDifficulty", "SPacketServerDifficulty");
public static final PacketType CHUNKS_BIOMES = new PacketType(PROTOCOL, SENDER, 0x0D, "ChunksBiomes", "ClientboundChunksBiomesPacket");
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0E, "ClearTitles");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0F, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x10, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x11, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x12, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x13, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x14, "SetSlot", "SPacketSetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x15, "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x16, "CustomChatCompletions");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x17, "CustomPayload", "SPacketCustomPayload");
public static final PacketType DAMAGE_EVENT = new PacketType(PROTOCOL, SENDER, 0x18, "DamageEvent", "ClientboundDamageEventPacket");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x19, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1A, "KickDisconnect", "SPacketDisconnect");
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x1B, "DisguisedChat");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1C, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1D, "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1E, "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1F, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x20, "OpenWindowHorse");
public static final PacketType HURT_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x21, "HurtAnimation", "ClientboundHurtAnimationPacket");
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x22, "InitializeBorder");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x23, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x24, "LevelChunkWithLight", "MapChunk", "SPacketChunkData");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x25, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x26, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x27, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x28, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x29, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x2A, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x2C, "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2D, "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2E, "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2F, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x30, "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x31, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x32, "Ping");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x33, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x34, "Abilities", "SPacketPlayerAbilities");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x35, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x36, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x37, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x38, "PlayerCombatKill");
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x39, "PlayerInfoRemove");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x3A, "PlayerInfoUpdate", "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x3B, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x3C, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x3D, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x3E, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x3F, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x40, "ResourcePackSend", "SPacketResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x41, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x42, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x43, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x44, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x45, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x46, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x47, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x48, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x49, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x4A, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4B, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x4C, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x4D, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x4E, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4F, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x50, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x51, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x52, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x53, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x54, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x55, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x56, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x57, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x58, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x59, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x5A, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x5B, "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x5C, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5D, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x5E, "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5F, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x60, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x61, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x62, "NamedSoundEffect");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x63, "StopSound");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x64, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x65, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x66, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x67, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x68, "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x69, "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x6A, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x6B, "UpdateEnabledFeatures");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x6C, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x6D, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x6E, "Tags");
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, "SpawnEntity", "SPacketSpawnObject");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x02, "NamedEntitySpawn", "SPacketSpawnPlayer");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x03, "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x04, "Statistic", "SPacketStatistics");
public static final PacketType BLOCK_CHANGED_ACK = new PacketType(PROTOCOL, SENDER, 0x05, "BlockChangedAck");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x07, "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x08, "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x09, "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0A, "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0B, "ServerDifficulty", "SPacketServerDifficulty");
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0C, "ClearTitles");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0D, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x0E, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0F, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x10, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x11, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x12, "SetSlot", "SPacketSetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x13, "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x14, "CustomChatCompletions");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x15, "CustomPayload", "SPacketCustomPayload");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x16, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x17, "KickDisconnect", "SPacketDisconnect");
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x18, "DisguisedChat");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x19, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1A, "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1B, "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1C, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x1D, "OpenWindowHorse");
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x1E, "InitializeBorder");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1F, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x20, "LevelChunkWithLight", "MapChunk", "SPacketChunkData");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x21, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x22, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x23, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x24, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x25, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x26, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x27, "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x28, "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2A, "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2B, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x2C, "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2D, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x2E, "Ping");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x2F, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x30, "Abilities", "SPacketPlayerAbilities");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x31, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x33, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x34, "PlayerCombatKill");
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x35, "PlayerInfoRemove");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x36, "PlayerInfoUpdate", "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x37, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x38, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x39, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x3A, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x3B, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x3C, "ResourcePackSend", "SPacketResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x3D, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x3E, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x3F, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x40, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x41, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x42, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x43, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x44, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x45, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x46, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x47, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x48, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x49, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x4A, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4B, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x4C, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x4D, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x4E, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x4F, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x50, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x51, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x52, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x53, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x54, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x55, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x56, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x57, "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x58, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x59, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x5A, "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5B, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x5C, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x5D, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x5E, "NamedSoundEffect");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x5F, "StopSound");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x60, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x61, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x62, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x63, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x64, "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x65, "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x66, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x67, "UpdateEnabledFeatures");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x68, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x69, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x6A, "Tags");
// ---- Removed in 1.9
@@ -367,7 +363,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x16, "CustomSoundEffect", "SPacketCustomSound");
private static final Server INSTANCE = new Server();
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(); }
@@ -385,7 +381,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public static class Client extends PacketTypeEnum {
private static final Sender SENDER = Sender.CLIENT;
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, "TeleportAccept", "CPacketConfirmTeleport");
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, "TileNBTQuery");
@@ -393,33 +389,33 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType CHAT_ACK = new PacketType(PROTOCOL, SENDER, 0x03, "ChatAck");
public static final PacketType CHAT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x04, "ChatCommand");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x05, "Chat", "CPacketChatMessage");
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x06, "ChatSessionUpdate");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x07, "ClientCommand", "CPacketClientStatus");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x08, "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x09, "TabComplete", "CPacketTabComplete");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x0A, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0B, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0C, "CloseWindow", "CPacketCloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0D, "CustomPayload", "CPacketCustomPayload");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0E, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0F, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x10, "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x11, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x12, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x13, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x15, "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x16, "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x17, "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x18, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x19, "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x1A, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x1B, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1C, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1D, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1E, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1F, "SteerVehicle", "CPacketInput");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x20, "Pong");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x06, "ClientCommand", "CPacketClientStatus");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x07, "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x08, "TabComplete", "CPacketTabComplete");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x09, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0A, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0B, "CloseWindow", "CPacketCloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0C, "CustomPayload", "CPacketCustomPayload");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0D, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0E, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0F, "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x10, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x11, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x12, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x13, "Flying$PacketPlayInPosition", "Flying$Position", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$PacketPlayInPositionLook", "Flying$PositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x15, "Flying$PacketPlayInLook", "Flying$Look", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x16, "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x17, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x18, "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x19, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x1A, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1B, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1C, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1D, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1E, "SteerVehicle", "CPacketInput");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x1F, "Pong");
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x20, "ChatSessionUpdate");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x21, "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x22, "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x23, "ItemName");
@@ -457,7 +453,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x06, "ChatPreview");
private static final Client INSTANCE = new Client();
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(); }
@@ -480,14 +476,14 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public static class Status {
static final Protocol PROTOCOL = Protocol.STATUS;
private static final Protocol PROTOCOL = Protocol.STATUS;
/**
* Outgoing packets.
* @author Kristian
*/
public static class Server extends PacketTypeEnum {
private static final Sender SENDER = Sender.SERVER;
private final static Sender SENDER = Sender.SERVER;
@ForceAsync
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, "ServerInfo", "SPacketServerInfo");
@@ -501,7 +497,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@ForceAsync
public static final PacketType OUT_SERVER_INFO = SERVER_INFO.clone();
private static final Server INSTANCE = new Server();
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(); }
@@ -519,13 +515,13 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public static class Client extends PacketTypeEnum {
private static final Sender SENDER = Sender.CLIENT;
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketServerQuery");
@ForceAsync
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x01, "Ping", "CPacketPing");
private static final Client INSTANCE = new Client();
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(); }
@@ -548,23 +544,22 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public static class Login {
static final Protocol PROTOCOL = Protocol.LOGIN;
private static final Protocol PROTOCOL = Protocol.LOGIN;
/**
* Outgoing packets.
* @author Kristian
*/
public static class Server extends PacketTypeEnum {
private static final Sender SENDER = Sender.SERVER;
private final static Sender SENDER = Sender.SERVER;
@ForceAsync
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, "Disconnect", "SPacketDisconnect");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "EncryptionBegin", "SPacketEncryptionRequest");
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, "Success", "SPacketLoginSuccess");
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, "SetCompression", "SPacketEnableCompression");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, "CustomPayload", "SPacketCustomPayload");
private static final Server INSTANCE = new Server();
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(); }
@@ -582,13 +577,13 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @author Kristian
*/
public static class Client extends PacketTypeEnum {
private static final Sender SENDER = Sender.CLIENT;
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketLoginStart");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "EncryptionBegin", "CPacketEncryptionResponse");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, "CustomPayload", "CPacketCustomPayload");
private static final Client INSTANCE = new Client();
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(); }
@@ -414,7 +414,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
} else if (methods.size() == 1) {
// We're in 1.2.5
alwaysSync = true;
} else if (MinecraftVersion.BOUNTIFUL_UPDATE.atOrAbove()) {
} else if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE)) {
// The centralized async marker was removed in 1.8
// Incoming chat packets can be async
if (event.getPacketType() == PacketType.Play.Client.CHAT) {
@@ -29,7 +29,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
public abstract class AbstractStructure {
protected transient Object handle;
@@ -309,31 +308,6 @@ public abstract class AbstractStructure {
BlockPosition.getConverter());
}
/**
* Retrieves a read/write structure for registrable objects.
*
* @param registrableClass The registrable object's class.
* @return A modifier for a registrable objects.
* @see MinecraftReflection#getBlockEntityTypeClass()
*/
public StructureModifier<WrappedRegistrable> getRegistrableModifier(
@NotNull final Class<?> registrableClass
) {
// Convert to and from the Bukkit wrapper
return structureModifier.withType(
registrableClass,
BukkitConverters.getWrappedRegistrable(registrableClass));
}
/**
* Retrieves a read/write structure for BlockEntityType.
* @return A modifier for a BlockEntityType.
*/
public StructureModifier<WrappedRegistrable> getBlockEntityTypeModifier() {
// Convert to and from the Bukkit wrapper
return getRegistrableModifier(MinecraftReflection.getBlockEntityTypeClass());
}
/**
* Retrieves a read/write structure for wrapped ChunkCoordIntPairs.
* @return A modifier for ChunkCoordIntPair.
@@ -787,31 +761,11 @@ public abstract class AbstractStructure {
EnumWrappers.getSoundCategoryConverter());
}
/**
* Retrieve a read/write structure for a Holder&lt;T&gt; in 1.19.3.
* @param genericType NMS type of T
* @param converter Converter from genericType to T
* @return A modifier for Holder fields
* @param <T> Bukkit type
*/
public <T> StructureModifier<T> getHolders(Class<?> genericType,
EquivalentConverter<T> converter) {
return structureModifier.withParamType(
MinecraftReflection.getHolderClass(),
Converters.holder(converter, WrappedRegistry.getRegistry(genericType)),
genericType
);
}
/**
* Retrieve a read/write structure for the SoundEffect enum in 1.9.
* @return A modifier for SoundEffect enum fields.
*/
public StructureModifier<Sound> getSoundEffects() {
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
return getHolders(MinecraftReflection.getSoundEffectClass(), BukkitConverters.getSoundConverter());
}
// Convert to and from Bukkit
return structureModifier.withType(
MinecraftReflection.getSoundEffectClass(),
@@ -972,12 +926,6 @@ public abstract class AbstractStructure {
);
}
public StructureModifier<List<UUID>> getUUIDLists() {
return structureModifier.withType(
List.class,
BukkitConverters.getListConverter(Converters.passthrough(UUID.class)));
}
/**
* Retrieve a read/write structure for Instants in (mostly for use in 1.19+)
* @return The Structure Modifier
@@ -987,7 +935,7 @@ public abstract class AbstractStructure {
}
/**
* Retrieve a read/write structure for profile public keys in 1.19
* Retrieve a read/write structure for profile public keys in 1.9
* @return The Structure Modifier
*/
public StructureModifier<WrappedProfilePublicKey> getProfilePublicKeys() {
@@ -997,24 +945,13 @@ public abstract class AbstractStructure {
}
/**
* Retrieve a read/write structure for profile public key data in 1.19
* Retrieve a read/write structure for profile public key data in 1.9
* @return The Structure Modifier
*/
public StructureModifier<WrappedProfileKeyData> getProfilePublicKeyData() {
return structureModifier.withType(
MinecraftReflection.getProfilePublicKeyDataClass(),
BukkitConverters.getWrappedPublicKeyDataConverter());
}
/**
* Retrieves read/write structure for remote chat session data in 1.19.3
* @return The Structure Modifier
*/
public StructureModifier<WrappedRemoteChatSessionData> getRemoteChatSessionData() {
return structureModifier.withType(
MinecraftReflection.getRemoteChatSessionDataClass(),
BukkitConverters.getWrappedRemoteChatSessionDataConverter()
);
MinecraftReflection.getProfilePublicKeyDataClass(),
BukkitConverters.getWrappedPublicKeyDataConverter());
}
/**
@@ -1043,7 +980,7 @@ public abstract class AbstractStructure {
}
/**
* @return read/writer structure direct access to salted signature data like chat messages
* @return read/writer structure direct access to signature data like chat messages
*/
public StructureModifier<WrappedSaltedSignature> getSignatures() {
return structureModifier.withType(
@@ -1052,16 +989,6 @@ public abstract class AbstractStructure {
);
}
/**
* @return read/writer structure direct access to unsalted signature data for example in chat message (since 1.19.3)
*/
public StructureModifier<WrappedMessageSignature> getMessageSignatures() {
return structureModifier.withType(
MinecraftReflection.getMessageSignatureClass(),
BukkitConverters.getWrappedMessageSignatureConverter()
);
}
/**
* @param leftConverter converter for left values
* @param rightConverter converter for right values
@@ -1168,12 +1095,6 @@ public abstract class AbstractStructure {
return structureModifier.withType(Optional.class, Converters.optional(converter));
}
public StructureModifier<Iterable<PacketContainer>> getPacketBundles() {
return structureModifier.withType(Iterable.class, Converters.iterable(
BukkitConverters.getPacketContainerConverter(), ArrayList::new, ArrayList::new
));
}
/**
* Represents an equivalent converter for ItemStack arrays.
* @author Kristian
@@ -8,7 +8,7 @@ import com.comphenix.protocol.wrappers.Converters;
public class InternalStructure extends AbstractStructure {
public InternalStructure(Object handle, StructureModifier<Object> structureModifier) {
protected InternalStructure(Object handle, StructureModifier<Object> structureModifier) {
super(handle, structureModifier);
}
@@ -40,8 +40,6 @@ import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import javax.annotation.Nullable;
/**
* Represents a packet sending or receiving event. Changes to the packet will be reflected in the final version to be
* sent or received. It is also possible to cancel an event.
@@ -72,9 +70,6 @@ public class PacketEvent extends EventObject implements Cancellable {
private boolean readOnly;
private boolean filtered;
@Nullable
private PacketEvent bundle;
/**
* Use the static constructors to create instances of this event.
*
@@ -86,31 +81,27 @@ public class PacketEvent extends EventObject implements Cancellable {
}
private PacketEvent(Object source, PacketContainer packet, Player player, boolean serverPacket) {
this(source, packet, null, player, serverPacket, true, null);
this(source, packet, null, player, serverPacket, true);
}
private PacketEvent(Object source, PacketContainer packet, NetworkMarker marker, Player player, boolean serverPacket,
boolean filtered, @Nullable PacketEvent bundleEvent) {
boolean filtered) {
super(source);
if(packet == null) {
throw new IllegalArgumentException("packet cannot be null");
}
this.packet = packet;
this.playerReference = new WeakReference<>(player);
this.networkMarker = marker;
this.serverPacket = serverPacket;
this.filtered = filtered;
this.bundle = bundleEvent;
}
private PacketEvent(PacketEvent original, AsyncMarker asyncMarker) {
super(original.source);
this.packet = original.packet;
this.playerReference = original.getPlayerReference();
this.cancel = original.cancel;
this.serverPacket = original.serverPacket;
this.filtered = original.filtered;
this.networkMarker = original.networkMarker;
private PacketEvent(PacketEvent origial, AsyncMarker asyncMarker) {
super(origial.source);
this.packet = origial.packet;
this.playerReference = origial.getPlayerReference();
this.cancel = origial.cancel;
this.serverPacket = origial.serverPacket;
this.filtered = origial.filtered;
this.networkMarker = origial.networkMarker;
this.asyncMarker = asyncMarker;
this.asynchronous = true;
}
@@ -137,7 +128,7 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return The event.
*/
public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client) {
return new PacketEvent(source, packet, marker, client, false, true, null);
return new PacketEvent(source, packet, marker, client, false, true);
}
/**
@@ -154,7 +145,7 @@ public class PacketEvent extends EventObject implements Cancellable {
*/
public static PacketEvent fromClient(Object source, PacketContainer packet, NetworkMarker marker, Player client,
boolean filtered) {
return new PacketEvent(source, packet, marker, client, false, filtered, null);
return new PacketEvent(source, packet, marker, client, false, filtered);
}
/**
@@ -179,7 +170,7 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return The event.
*/
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient) {
return new PacketEvent(source, packet, marker, recipient, true, true, null);
return new PacketEvent(source, packet, marker, recipient, true, true);
}
/**
@@ -196,25 +187,7 @@ public class PacketEvent extends EventObject implements Cancellable {
*/
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient,
boolean filtered) {
return fromServer(source, packet, marker, recipient, filtered, null);
}
/**
* Creates an event representing a server packet transmission.
* <p>
* If <i>filtered</i> is FALSE, then this event is only processed by packet monitors.
*
* @param source - the event source.
* @param packet - the packet.
* @param marker - the network marker.
* @param recipient - the client that will receieve the packet.
* @param filtered - whether this packet event is processed by every packet listener.
* @param bundle - The corresponding packet event of the bundle if this packet is part of a bundle. Otherwise, null.
* @return The event.
*/
public static PacketEvent fromServer(Object source, PacketContainer packet, NetworkMarker marker, Player recipient,
boolean filtered, @Nullable PacketEvent bundle) {
return new PacketEvent(source, packet, marker, recipient, true, filtered, bundle);
return new PacketEvent(source, packet, marker, recipient, true, filtered);
}
/**
@@ -544,15 +517,6 @@ public class PacketEvent extends EventObject implements Cancellable {
}
}
/**
* Returns the packet event corresponding to the bundle if this packet is sent as a part of a bundle, t. Otherwise, null.
* @return Corresponding packet event or null.
*/
@Nullable
public PacketEvent getBundle() {
return bundle;
}
@Override
public String toString() {
return "PacketEvent[player=" + getPlayer() + ", packet=" + packet + "]";
@@ -28,7 +28,7 @@ public interface PacketOutputHandler {
* Note that the buffer is initially filled with the output from the default write method.
* <p>
* In Minecraft 1.6.4, the header is always excluded, whereas it MUST be included in Minecraft 1.7.2. Call
* NetworkMarker#requireOutputHeader() to determine this.
* {@link NetworkMarker#requireOutputHeader()} to determine this.
* @param event - the packet that will be outputted.
* @param buffer - the data that is currently scheduled to be outputted.
* @return The modified byte array to write. NULL is not permitted.
@@ -178,9 +178,7 @@ class EntityUtilities {
Validate.notNull(entity, "entity cannot be null");
Object trackerEntry = this.getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
if (trackerEntry == null) { //entity.toString() is too expensive to be called every time
throw new IllegalArgumentException("Could not find entity trackers for " + entity);
}
Validate.notNull(trackerEntry, "Could not find entity trackers for " + entity);
if (this.trackedPlayersField == null) {
this.trackedPlayersField = Accessors.getFieldAccessor(
@@ -3,7 +3,6 @@ package com.comphenix.protocol.injector;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
@@ -33,8 +32,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
@@ -309,10 +306,6 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
ListeningWhitelist outbound = listener.getSendingWhitelist();
ListeningWhitelist inbound = listener.getReceivingWhitelist();
// Remove packets from the lists if they are not supposed to be in those lists
inbound.getTypes().removeIf(type -> !type.isClient());
outbound.getTypes().removeIf(type -> !type.isServer());
// verify plugin if needed
if (this.shouldVerifyPlugin(outbound, inbound)) {
this.printPluginWarnings(listener.getPlugin());
@@ -544,29 +537,25 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
}
private void postPacketToListeners(SortedPacketListenerList listeners, PacketEvent event, boolean outbound) {
try {
// append async marker if any async listener for the packet was registered
if (this.asyncFilterManager.hasAsynchronousListeners(event)) {
event.setAsyncMarker(this.asyncFilterManager.createAsyncMarker());
}
// append async marker if any async listener for the packet was registered
if (this.asyncFilterManager.hasAsynchronousListeners(event)) {
event.setAsyncMarker(this.asyncFilterManager.createAsyncMarker());
}
// post to sync listeners
if (outbound) {
listeners.invokePacketSending(this.reporter, event);
} else {
listeners.invokePacketRecieving(this.reporter, event);
}
// post to sync listeners
if (outbound) {
listeners.invokePacketSending(this.reporter, event);
} else {
listeners.invokePacketRecieving(this.reporter, event);
}
// check if we need to post the packet to the async handler
if (!event.isCancelled() && event.getAsyncMarker() != null && !event.getAsyncMarker().isAsyncCancelled()) {
this.asyncFilterManager.enqueueSyncPacket(event, event.getAsyncMarker());
// check if we need to post the packet to the async handler
if (!event.isCancelled() && event.getAsyncMarker() != null && !event.getAsyncMarker().isAsyncCancelled()) {
this.asyncFilterManager.enqueueSyncPacket(event, event.getAsyncMarker());
// cancel the packet here for async processing (enqueueSyncPacket will create a copy of the event)
event.setReadOnly(false);
event.setCancelled(true);
}
} catch (Throwable t) {
plugin.getLogger().log(Level.WARNING, "Failed to process " + (outbound ? "outbound" : "inbound") + " packet event: " + event, t);
// cancel the packet here for async processing (enqueueSyncPacket will create a copy of the event)
event.setReadOnly(false);
event.setCancelled(true);
}
}
@@ -17,25 +17,17 @@
package com.comphenix.protocol.injector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.timing.TimedListenerManager;
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
import com.comphenix.protocol.timing.TimedTracker;
import javax.annotation.Nullable;
/**
* Registry of synchronous packet listeners.
*
@@ -116,12 +108,14 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
* @param event - the related packet event.
* @param element - the listener to invoke.
*/
private void invokeReceivingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
private final void invokeReceivingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
try {
event.setReadOnly(element.getPriority() == ListenerPriority.MONITOR);
element.getListener().onPacketReceiving(event);
} catch (OutOfMemoryError | ThreadDeath e) {
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Minecraft doesn't want your Exception.
@@ -136,89 +130,73 @@ public final class SortedPacketListenerList extends AbstractConcurrentListenerMu
* @param event - the packet event to invoke.
*/
public void invokePacketSending(ErrorReporter reporter, PacketEvent event) {
invokePacketSending(reporter, event, null);
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
if (list == null)
return;
if (timedManager.isTiming()) {
for (PrioritizedListener<PacketListener> element : list) {
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_SERVER_SIDE);
long token = tracker.beginTracking();
// Measure and record the execution time
invokeSendingListener(reporter, event, element);
tracker.endTracking(token, event.getPacketType());
}
} else {
for (PrioritizedListener<PacketListener> element : list) {
invokeSendingListener(reporter, event, element);
}
}
}
/**
* Invokes the given packet event for every registered listener of the given priority.
* @param reporter - the error reporter that will be used to inform about listener exceptions.
* @param event - the packet event to invoke.
* @param priorityFilter - the priority for a listener to be invoked. If null is provided, every registered listener will be invoked
* @param priorityFilter - the required priority for a listener to be invoked.
*/
public void invokePacketSending(ErrorReporter reporter, PacketEvent event, @Nullable ListenerPriority priorityFilter) {
invokeUnpackedPacketSending(reporter, event, priorityFilter);
if (event.getPacketType() == PacketType.Play.Server.BUNDLE && !event.isCancelled()) {
// unpack the bundle and invoke for each packet in the bundle
Iterable<PacketContainer> packets = event.getPacket().getPacketBundles().read(0);
List<PacketContainer> outPackets = new ArrayList<>();
for (PacketContainer subPacket : packets) {
if(subPacket == null) {
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "Failed to invoke packet event " + (priorityFilter == null ? "" : ("with priority " + priorityFilter)) + " in bundle because bundle contains null packet: " + packets, new Throwable());
continue;
}
PacketEvent subPacketEvent = PacketEvent.fromServer(this, subPacket, event.getNetworkMarker(), event.getPlayer());
invokeUnpackedPacketSending(reporter, subPacketEvent, priorityFilter);
if (!subPacketEvent.isCancelled()) {
// if the packet event has been cancelled, the packet will be removed from the bundle
PacketContainer packet = subPacketEvent.getPacket();
if(packet == null) {
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "null packet container returned for " + subPacketEvent, new Throwable());
} else if(packet.getHandle() == null) {
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "null packet handle returned for " + subPacketEvent, new Throwable());
} else {
outPackets.add(packet);
}
}
}
if (packets.iterator().hasNext()) {
event.getPacket().getPacketBundles().write(0, outPackets);
} else {
// cancel entire packet if each individual packet has been cancelled
event.setCancelled(true);
}
}
}
private void invokeUnpackedPacketSending(ErrorReporter reporter, PacketEvent event, @org.jetbrains.annotations.Nullable ListenerPriority priorityFilter) {
public void invokePacketSending(ErrorReporter reporter, PacketEvent event, ListenerPriority priorityFilter) {
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
if (list == null)
return;
if (timedManager.isTiming()) {
for (PrioritizedListener<PacketListener> element : list) {
if (priorityFilter == null || element.getPriority() == priorityFilter) {
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_SERVER_SIDE);
long token = tracker.beginTracking();
// Measure and record the execution time
invokeSendingListener(reporter, event, element);
tracker.endTracking(token, event.getPacketType());
if (element.getPriority() == priorityFilter) {
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_SERVER_SIDE);
long token = tracker.beginTracking();
// Measure and record the execution time
invokeSendingListener(reporter, event, element);
tracker.endTracking(token, event.getPacketType());
}
}
} else {
for (PrioritizedListener<PacketListener> element : list) {
if (priorityFilter == null || element.getPriority() == priorityFilter) {
if (element.getPriority() == priorityFilter) {
invokeSendingListener(reporter, event, element);
}
}
}
}
/**
* Invoke a particular sending listener.
* @param reporter - the error reporter.
* @param event - the related packet event.
* @param element - the listener to invoke.
*/
private void invokeSendingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
private final void invokeSendingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
try {
event.setReadOnly(element.getPriority() == ListenerPriority.MONITOR);
element.getListener().onPacketSending(event);
} catch (OutOfMemoryError | ThreadDeath e) {
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Minecraft doesn't want your Exception.
@@ -54,9 +54,7 @@ public class StructureCache {
private static final Map<PacketType, StructureModifier<Object>> STRUCTURE_MODIFIER_CACHE = new ConcurrentHashMap<>();
// packet data serializer which always returns an empty nbt tag compound
private static final Object TRICK_INIT_LOCK = new Object();
private static boolean TRICK_TRIED = false;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_BASE;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_JSON;
@@ -137,12 +135,6 @@ public class StructureCache {
return STRUCTURE_MODIFIER_CACHE.computeIfAbsent(packetType, type -> {
Class<?> packetClass = PacketRegistry.getPacketClassFromType(type);
// We need to map the Bundle Delimiter to the synthetic bundle packet which contains a list of all packets in a bundle
if (MinecraftReflection.isBundleDelimiter(packetClass)) {
packetClass = MinecraftReflection.getPackedBundlePacketClass().get();
}
return new StructureModifier<>(packetClass, MinecraftReflection.getPacketClass(), true);
});
}
@@ -166,50 +158,45 @@ public class StructureCache {
return TRICKED_DATA_SERIALIZER_BASE != null;
}
synchronized (TRICK_INIT_LOCK) {
if (TRICK_TRIED) {
return TRICKED_DATA_SERIALIZER_BASE != null;
}
// prevent double init
TRICK_TRIED = true;
try {
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
Object textCompound = WrappedChatComponent.fromText("").getHandle();
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
// base builder which intercepts a few methods
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
.intercept(FixedValue.value(compound))
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
.intercept(FixedValue.value(textCompound))
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
.intercept(FixedValue.nullValue());
Class<?> serializerBase = baseBuilder.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
try {
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
Object textCompound = WrappedChatComponent.fromText("").getHandle();
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
// base builder which intercepts a few methods
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
.intercept(FixedValue.value(compound))
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
.intercept(FixedValue.value(textCompound))
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
.intercept(FixedValue.nullValue());
Class<?> serializerBase = baseBuilder.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
// extended builder which intercepts the read string method as well
Class<?> withStringIntercept = baseBuilder
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
.intercept(FixedValue.value("{}"))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
// extended builder which intercepts the read string method as well
Class<?> withStringIntercept = baseBuilder
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
.intercept(FixedValue.value("{}"))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
// worked
return true;
} catch (Exception ignored) {
} finally {
TRICK_TRIED = true;
}
// didn't work
return false;
// worked
return true;
} catch (Exception ignored) {
}
// didn't work
return false;
}
}
@@ -27,7 +27,12 @@ import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.*;
import com.comphenix.protocol.utility.ByteBuddyGenerated;
import com.comphenix.protocol.utility.MinecraftFields;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
@@ -96,7 +101,7 @@ public class NettyChannelInjector implements Injector {
// packet marking
private final Map<Object, NetworkMarker> savedMarkers = new WeakHashMap<>(16, 0.9f);
private final Set<Object> skippedPackets = ConcurrentHashMap.newKeySet();
private final Set<Object> skippedPackets = Collections.synchronizedSet(new HashSet<>());
protected final ThreadLocal<Boolean> processedPackets = ThreadLocal.withInitial(() -> Boolean.FALSE);
// status of this injector
@@ -548,7 +553,7 @@ public class NettyChannelInjector implements Injector {
}
// no listener and no marker - no magic :)
if (!this.channelListener.hasListener(packet.getClass()) && marker == null && !MinecraftReflection.isBundlePacket(packet.getClass())) {
if (!this.channelListener.hasListener(packet.getClass()) && marker == null) {
return action;
}
@@ -28,7 +28,6 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.Util;
import com.comphenix.protocol.wrappers.Pair;
import io.netty.channel.ChannelFuture;
import org.bukkit.Server;
@@ -91,7 +90,7 @@ public class NetworkManagerInjector implements ChannelListener {
public PacketEvent onPacketSending(Injector injector, Object packet, NetworkMarker marker) {
// check if we need to intercept the packet
Class<?> packetClass = packet.getClass();
if (marker != null || MinecraftReflection.isBundlePacket(packetClass) || outboundListeners.contains(packetClass)) {
if (this.outboundListeners.contains(packetClass) || marker != null) {
// wrap packet and construct the event
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(packetClass), packet);
PacketEvent packetEvent = PacketEvent.fromServer(this, container, marker, injector.getPlayer());
@@ -109,7 +108,7 @@ public class NetworkManagerInjector implements ChannelListener {
public PacketEvent onPacketReceiving(Injector injector, Object packet, NetworkMarker marker) {
// check if we need to intercept the packet
Class<?> packetClass = packet.getClass();
if (marker != null || inboundListeners.contains(packetClass)) {
if (this.inboundListeners.contains(packetClass) || marker != null) {
// wrap the packet and construct the event
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(packetClass), packet);
PacketEvent packetEvent = PacketEvent.fromClient(this, container, marker, injector.getPlayer());
@@ -45,12 +45,12 @@ public class PacketRegistry {
*/
protected static class Register {
// The main lookup table
final Map<PacketType, Optional<Class<?>>> typeToClass = new ConcurrentHashMap<>();
final Map<Class<?>, PacketType> classToType = new ConcurrentHashMap<>();
private final Map<PacketType, Optional<Class<?>>> typeToClass = new ConcurrentHashMap<>();
private final Map<Class<?>, PacketType> classToType = new ConcurrentHashMap<>();
volatile Set<PacketType> serverPackets = new HashSet<>();
volatile Set<PacketType> clientPackets = new HashSet<>();
final List<MapContainer> containers = new ArrayList<>();
private volatile Set<PacketType> serverPackets = new HashSet<>();
private volatile Set<PacketType> clientPackets = new HashSet<>();
private final List<MapContainer> containers = new ArrayList<>();
public Register() {}
@@ -251,13 +251,10 @@ public class PacketRegistry {
protected static void associatePackets(Register register, Map<Integer, Class<?>> lookup, PacketType.Protocol protocol, Sender sender) {
for (Map.Entry<Integer, Class<?>> entry : lookup.entrySet()) {
int packetId = entry.getKey();
Class<?> packetClass = entry.getValue();
PacketType type = PacketType.fromCurrent(protocol, sender, packetId, packetClass);
PacketType type = PacketType.fromCurrent(protocol, sender, entry.getKey(), entry.getValue());
try {
register.registerPacket(type, packetClass, sender);
register.registerPacket(type, entry.getValue(), sender);
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception associating packet " + type, ex);
}
@@ -273,28 +270,18 @@ public class PacketRegistry {
}
}
private static final Object registryLock = new Object();
/**
* Initializes the packet registry.
*/
private static void initialize() {
if (INITIALIZED) {
return;
}
synchronized (registryLock) {
if (INITIALIZED) {
return;
}
if (!INITIALIZED) {
INITIALIZED = true;
if (MinecraftVersion.BEE_UPDATE.atOrAbove()) {
REGISTER = createNewRegister();
} else {
REGISTER = createOldRegister();
}
INITIALIZED = true;
}
}
@@ -363,9 +350,6 @@ public class PacketRegistry {
// Try the lookup first (may be null, so check contains)
Optional<Class<?>> res = REGISTER.typeToClass.get(type);
if (res != null) {
if(res.isPresent() && MinecraftReflection.isBundleDelimiter(res.get())) {
return MinecraftReflection.getPackedBundlePacketClass();
}
return res;
}
@@ -378,9 +362,6 @@ public class PacketRegistry {
// cache it for next time
associate(type, clazz);
if(clazz != null && MinecraftReflection.isBundleDelimiter(clazz)) {
clazz = MinecraftReflection.getPackedBundlePacketClass().orElseThrow(() -> new IllegalStateException("Packet bundle class not found."));
}
return Optional.ofNullable(clazz);
}
@@ -402,11 +383,6 @@ public class PacketRegistry {
*/
public static PacketType getPacketType(Class<?> packet) {
initialize();
if (MinecraftReflection.isBundlePacket(packet)) {
return PacketType.Play.Server.BUNDLE;
}
return REGISTER.classToType.get(packet);
}
@@ -23,11 +23,6 @@ import com.comphenix.protocol.reflect.instances.BannedGenerator;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.InstanceProvider;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
@@ -156,34 +151,12 @@ public class StructureModifier<T> {
return requireDefaults;
}
// Used to filter out irrelevant fields
private static final ThreadLocal<Table<Class<?>, Class<?>, Reference<List<FieldAccessor>>>>
fieldCacheLocal = ThreadLocal.withInitial(HashBasedTable::create);
private static final Class<?> NULL_CACHE_CLASS_REPLACEMENT = Void.class;
// Used to filter out irrelevant fields
private static List<FieldAccessor> getFields(Class<?> type, Class<?> superclassExclude) {
if (type == null) {
throw new IllegalArgumentException("Type cannot be NULL.");
}
Table<Class<?>, Class<?>, Reference<List<FieldAccessor>>> fieldCache = fieldCacheLocal.get();
Class<?> superclassKey = superclassExclude == null ? NULL_CACHE_CLASS_REPLACEMENT : superclassExclude;
Reference<List<FieldAccessor>> cacheEntryReference = fieldCache.get(type, superclassKey);
if (cacheEntryReference != null) {
List<FieldAccessor> cacheEntry = cacheEntryReference.get();
if (cacheEntry != null) {
return cacheEntry;
}
}
List<FieldAccessor> accessors = FuzzyReflection.fromClass(type, true)
.getDeclaredFields(superclassExclude)
.stream()
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.map(Accessors::getFieldAccessor)
.collect(Collectors.toList());
fieldCache.put(type, superclassKey, new SoftReference<>(accessors));
fieldCache.cellSet().removeIf(entry -> entry.getValue().get() == null);
return accessors;
return FuzzyReflection.fromClass(type, true).getDeclaredFields(superclassExclude).stream()
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.map(Accessors::getFieldAccessor)
.collect(Collectors.toList());
}
/**
@@ -4,8 +4,6 @@ import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import com.google.common.base.Preconditions;
final class DefaultFieldAccessor implements FieldAccessor {
private final Field field;
@@ -15,9 +13,9 @@ final class DefaultFieldAccessor implements FieldAccessor {
private final MethodHandle getter;
public DefaultFieldAccessor(Field field, MethodHandle setter, MethodHandle getter, boolean staticField) {
this.field = Preconditions.checkNotNull(field, "field");
this.setter = Preconditions.checkNotNull(setter, "setter");
this.getter = Preconditions.checkNotNull(getter, "getter");
this.field = field;
this.setter = setter;
this.getter = getter;
this.staticField = staticField;
}
@@ -11,8 +11,6 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.logging.Level;
import com.google.common.base.Preconditions;
final class MethodHandleHelper {
private static final Lookup LOOKUP;
@@ -53,35 +51,29 @@ final class MethodHandleHelper {
}
public static MethodAccessor getMethodAccessor(Method method) {
Preconditions.checkNotNull(method, "method");
try {
MethodHandle unreflected = LOOKUP.unreflect(method);
boolean staticMethod = Modifier.isStatic(method.getModifiers());
MethodHandle generified = convertToGeneric(unreflected, staticMethod, false);
return new DefaultMethodAccessor(method, generified, staticMethod);
} catch (IllegalAccessException ex) {
throw new IllegalStateException("Unable to access method " + method, ex);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Unable to access method " + method);
}
}
public static ConstructorAccessor getConstructorAccessor(Constructor<?> constructor) {
Preconditions.checkNotNull(constructor, "constructor");
try {
MethodHandle unreflected = LOOKUP.unreflectConstructor(constructor);
MethodHandle generified = convertToGeneric(unreflected, false, true);
return new DefaultConstrutorAccessor(constructor, generified);
} catch (IllegalAccessException ex) {
throw new IllegalStateException("Unable to access constructor " + constructor, ex);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Unable to access constructor " + constructor);
}
}
public static FieldAccessor getFieldAccessor(Field field) {
Preconditions.checkNotNull(field, "field");
try {
boolean staticField = Modifier.isStatic(field.getModifiers());
@@ -105,18 +97,10 @@ final class MethodHandleHelper {
setter = setter.asType(VIRTUAL_FIELD_SETTER);
}
if (getter == null) {
throw new IllegalStateException("Unable to access field " + field + ". Could not find getter");
}
if (setter == null) {
throw new IllegalStateException("Unable to access field " + field + ". Could not find setter");
}
return new DefaultFieldAccessor(field, setter, getter, staticField);
} catch (IllegalAccessException | NoSuchFieldException ex) {
} catch (IllegalAccessException | NoSuchFieldException exception) {
// NoSuchFieldException can never happen, the field always exists
throw new IllegalStateException("Unable to access field " + field, ex);
throw new IllegalStateException("Unable to access field " + field);
}
}
@@ -36,7 +36,7 @@ final class CachedPackage {
* @param packageName - the name of the current package.
* @param source - the class source.
*/
CachedPackage(String packageName, ClassSource source) {
public CachedPackage(String packageName, ClassSource source) {
this.source = source;
this.packageName = packageName;
this.cache = new ConcurrentHashMap<>();
@@ -71,10 +71,6 @@ final class CachedPackage {
}
}
private Optional<Class<?>> resolveClass(String className) {
return source.loadClass(combine(packageName, className));
}
/**
* Retrieve the class object of a specific class in the current package.
*
@@ -82,21 +78,13 @@ final class CachedPackage {
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
public Optional<Class<?>> getPackageClass(String className, String... aliases) {
return cache.computeIfAbsent(className, x -> {
Optional<Class<?>> clazz = resolveClass(className);
if (clazz.isPresent()) {
return clazz;
public Optional<Class<?>> getPackageClass(final String className) {
return this.cache.computeIfAbsent(className, x -> {
try {
return Optional.ofNullable(this.source.loadClass(combine(this.packageName, className)));
} catch (ClassNotFoundException ex) {
return Optional.empty();
}
for (String alias : aliases) {
clazz = resolveClass(alias);
if (clazz.isPresent()) {
return clazz;
}
}
return Optional.empty();
});
}
}
@@ -2,8 +2,6 @@ package com.comphenix.protocol.utility;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
/**
* Represents an abstract class loader that can only retrieve classes by their canonical name.
@@ -39,25 +37,27 @@ public interface ClassSource {
* @return The corresponding class source.
*/
static ClassSource fromClassLoader(final ClassLoader loader) {
return canonicalName -> {
try {
return Optional.of(loader.loadClass(canonicalName));
} catch (ClassNotFoundException ignored) {
return Optional.empty();
}
};
return loader::loadClass;
}
/**
* Construct a class source from a mapping of canonical names and the corresponding classes. If the map is null, it
* will be interpreted as an empty map. If the map does not contain a Class with the specified name, or that string
* maps to NULL explicitly, an empty optional will be returned
* maps to NULL explicitly, a {@link ClassNotFoundException} will be thrown.
*
* @param map - map of class names and classes.
* @return The class source.
*/
static ClassSource fromMap(final Map<String, Class<?>> map) {
return canonicalName -> Optional.ofNullable(map.get(canonicalName));
return canonicalName -> {
Class<?> loaded = map == null ? null : map.get(canonicalName);
if (loaded == null) {
// Throw the appropriate exception if we can't load the class
throw new ClassNotFoundException("The specified class could not be found by this ClassLoader.");
}
return loaded;
};
}
/**
@@ -94,8 +94,14 @@ public interface ClassSource {
* @param other - the other class source.
* @return A new class source.
*/
default ClassSource retry(ClassSource other) {
return canonicalName -> Optionals.or(loadClass(canonicalName), () -> other.loadClass(canonicalName));
default ClassSource retry(final ClassSource other) {
return canonicalName -> {
try {
return ClassSource.this.loadClass(canonicalName);
} catch (ClassNotFoundException e) {
return other.loadClass(canonicalName);
}
};
}
/**
@@ -109,9 +115,12 @@ public interface ClassSource {
}
/**
* Retrieve a class by its canonical name
* @param canonicalName The class's canonical name, i.e. java.lang.Object
* @return Optional that may contain a Class
* Retrieve a class by name.
*
* @param canonicalName - the full canonical name of the class.
* @return The corresponding class. If the class is not found, NULL should <b>not</b> be returned, instead a {@code
* ClassNotFoundException} exception should be thrown.
* @throws ClassNotFoundException If the class could not be found.
*/
Optional<Class<?>> loadClass(String canonicalName);
Class<?> loadClass(String canonicalName) throws ClassNotFoundException;
}
@@ -85,7 +85,6 @@ public final class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 19, 1), 760);
map.put(new MinecraftVersion(1, 19, 2), 760);
map.put(new MinecraftVersion(1, 19, 3), 761);
map.put(new MinecraftVersion(1, 19, 4), 762);
return map;
}
@@ -25,7 +25,6 @@ import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -629,22 +628,6 @@ public final class MinecraftReflection {
return getMinecraftClass("network.chat.IChatBaseComponent", "network.chat.IChatbaseComponent", "network.chat.Component", "IChatBaseComponent");
}
public static Optional<Class<?>> getPackedBundlePacketClass() {
return getOptionalNMS("network.protocol.game.ClientboundBundlePacket", "ClientboundBundlePacket");
}
public static boolean isBundlePacket(Class<?> packetClass) {
return Optionals.Equals(getPackedBundlePacketClass(), packetClass);
}
public static boolean isBundleDelimiter(Class<?> packetClass) {
return Optionals.Equals(getBundleDelimiterClass(), packetClass);
}
public static Optional<Class<?>> getBundleDelimiterClass() {
return getOptionalNMS("network.protocol.BundleDelimiterPacket","BundleDelimiterPacket");
}
public static Class<?> getIChatBaseComponentArrayClass() {
return getArrayClass(getIChatBaseComponentClass());
}
@@ -1342,8 +1325,11 @@ public final class MinecraftReflection {
* @return The class.
*/
private static Class<?> getClass(String className) {
return getClassSource().loadClass(className)
.orElseThrow(() -> new RuntimeException("Cannot find class " + className));
try {
return getClassSource().loadClass(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot find class " + className, e);
}
}
/**
@@ -1378,34 +1364,12 @@ public final class MinecraftReflection {
.orElseThrow(() -> new RuntimeException("Failed to find NMS class: " + className));
}
/**
* Optionally retrieve the class object of a NMS (net.minecraft.server) class.
* If the class does not exist, the optional will be empty
*
* @param className NMS class name
* @param aliases Potential aliases
* @return Optional that may contain the class
*/
private static Optional<Class<?>> getOptionalNMS(String className, String... aliases) {
if (minecraftPackage == null) {
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
}
return minecraftPackage.getPackageClass(className, aliases);
}
/**
* Retrieves a nullable NMS (net.minecraft.server) class. We will attempt to
* look up the class and its aliases, but will return null if none is found.
*
* @deprecated - Use getOptionalNMS where possible
* @param className NMS class name
* @param aliases Potential aliases
* @return The class, or null if not found
*/
@Deprecated
public static Class<?> getNullableNMS(String className, String... aliases) {
return getOptionalNMS(className, aliases).orElse(null);
try {
return getMinecraftClass(className, aliases);
} catch (RuntimeException ex) {
return null;
}
}
/**
@@ -1442,8 +1406,29 @@ public final class MinecraftReflection {
* @throws RuntimeException If we are unable to find any of the given classes.
*/
public static Class<?> getMinecraftClass(String className, String... aliases) {
return getOptionalNMS(className, aliases)
.orElseThrow(() -> new RuntimeException(String.format("Unable to find %s (%s)", className, String.join(", ", aliases))));
if (minecraftPackage == null) {
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
}
return minecraftPackage.getPackageClass(className).orElseGet(() -> {
Class<?> resolved = null;
for (String alias : aliases) {
// try to resolve the class and stop searching if we found it
resolved = minecraftPackage.getPackageClass(alias).orElse(null);
if (resolved != null) {
break;
}
}
// if we resolved the class cache it and return the result
if (resolved != null) {
minecraftPackage.setPackageClass(className, resolved);
return resolved;
}
// unable to find the class
throw new RuntimeException(String.format("Unable to find %s (%s)", className, String.join(", ", aliases)));
});
}
/**
@@ -1573,10 +1558,6 @@ public final class MinecraftReflection {
return getMinecraftClass("world.entity.player.ProfilePublicKey");
}
public static Class<?> getMessageSignatureClass() {
return getMinecraftClass("network.chat.MessageSignature");
}
public static Class<?> getSaltedSignatureClass() {
try {
return getMinecraftClass("SaltedSignature");
@@ -1625,10 +1606,6 @@ public final class MinecraftReflection {
return getMinecraftClass("network.chat.RemoteChatSession");
}
public static Class<?> getRemoteChatSessionDataClass() {
return getRemoteChatSessionClass().getClasses()[0];
}
public static Class<?> getFastUtilClass(String className) {
return getLibraryClass("it.unimi.dsi.fastutil." + className);
}
@@ -1692,24 +1669,4 @@ public final class MinecraftReflection {
return infoClass;
}
}
public static Class<?> getDynamicOpsClass() {
return getLibraryClass("com.mojang.serialization.DynamicOps");
}
public static Class<?> getJsonOpsClass() {
return getLibraryClass("com.mojang.serialization.JsonOps");
}
public static Class<?> getNbtOpsClass() {
return getMinecraftClass("nbt.DynamicOpsNBT" /* Spigot Mappings */, "nbt.NbtOps" /* Mojang Mappings */);
}
public static Class<?> getCodecClass() {
return getLibraryClass("com.mojang.serialization.Codec");
}
public static Class<?> getHolderClass() {
return getMinecraftClass("core.Holder");
}
}
@@ -37,11 +37,6 @@ import org.bukkit.Server;
*/
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
/**
* Version 1.19.4 - the rest of the feature preview
*/
public static final MinecraftVersion FEATURE_PREVIEW_2 = new MinecraftVersion("1.19.4");
/**
* Version 1.19.3 - introducing feature preview
*/
@@ -58,10 +53,6 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
* Version 1.17 - caves and cliffs part 1
*/
public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17");
/**
* Version 1.16.4
*/
public static final MinecraftVersion NETHER_UPDATE_4 = new MinecraftVersion("1.16.4");
/**
* Version 1.16.2 - breaking change to the nether update
*/
@@ -126,7 +117,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = FEATURE_PREVIEW_2;
public static final MinecraftVersion LATEST = FEATURE_PREVIEW_UPDATE;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;
@@ -277,7 +268,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
currentVersion = version;
}
private static boolean atOrAbove(MinecraftVersion version) {
public static boolean atOrAbove(MinecraftVersion version) {
return getCurrentVersion().isAtLeast(version);
}
@@ -358,7 +349,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
*/
public boolean atOrAbove() {
if (this.atCurrentOrAbove == null) {
this.atCurrentOrAbove = atOrAbove(this);
this.atCurrentOrAbove = MinecraftVersion.atOrAbove(this);
}
return this.atCurrentOrAbove;
@@ -1,45 +0,0 @@
package com.comphenix.protocol.utility;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* Utility methods for operating with Optionals
*/
public final class Optionals {
/**
* Chains two optionals together by returning the secondary
* optional if the primary does not contain a value
* @param primary Primary optional
* @param secondary Supplier of secondary optional
* @return The resulting optional
* @param <T> Type
*/
public static <T> Optional<T> or(Optional<T> primary, Supplier<Optional<T>> secondary) {
return primary.isPresent() ? primary : secondary.get();
}
/**
* Evaluates the provided predicate against the optional only if it is present
* @param optional Optional
* @param predicate Test to run against potential value
* @return True if the optional is present and the predicate passes
* @param <T> Type
*/
public static <T> boolean TestIfPresent(Optional<T> optional, Predicate<T> predicate) {
return optional.isPresent() && predicate.test(optional.get());
}
/**
* Check if the optional has a value and its value equals the provided value
* @param optional Optional
* @param contents Contents to test for
* @return True if the optional has a value and that value equals the parameter
* @param <T> Type
*/
public static <T> boolean Equals(Optional<T> optional, Class<?> contents) {
return optional.isPresent() && contents.equals(optional.get());
}
}
@@ -22,7 +22,6 @@ package com.comphenix.protocol.utility;
public final class Util {
private static final boolean SPIGOT = classExists("org.spigotmc.SpigotConfig");
private static Class<?> cachedBundleClass;
public static boolean classExists(String className) {
try {
@@ -40,7 +40,7 @@ public class AdventureComponentConverter {
/**
* Converts a {@link Component} into a ProtocolLib wrapper
* @param component Component
* @param components Component
* @return ProtocolLib wrapper
*/
public static WrappedChatComponent fromComponent(Component component) {
@@ -20,7 +20,6 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.base.Defaults;
import com.google.common.base.Preconditions;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@@ -89,8 +88,6 @@ public class AutoWrapper<T> implements EquivalentConverter<T> {
}
public T wrap(Object nmsObject) {
Preconditions.checkNotNull(nmsObject);
T instance;
try {
@@ -117,8 +114,6 @@ public class AutoWrapper<T> implements EquivalentConverter<T> {
}
public Object unwrap(Object wrapper) {
Preconditions.checkNotNull(wrapper);
// ensures that all accessors are present
computeFieldAccessors();
computeNmsConstructorAccess();
@@ -144,14 +139,14 @@ public class AutoWrapper<T> implements EquivalentConverter<T> {
nmsAccessors = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.map(Accessors::getFieldAccessor)
.map(field -> Accessors.getFieldAccessor(field))
.toArray(FieldAccessor[]::new);
}
if (wrapperAccessors == null) {
wrapperAccessors = Arrays
.stream(wrapperClass.getDeclaredFields())
.map(Accessors::getFieldAccessor)
.map(field -> Accessors.getFieldAccessor(field))
.toArray(FieldAccessor[]::new);
}
}
@@ -16,7 +16,6 @@
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.Either.Left;
import com.comphenix.protocol.wrappers.Either.Right;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
@@ -77,7 +76,6 @@ import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import static com.comphenix.protocol.utility.MinecraftReflection.getCraftBukkitClass;
import static com.comphenix.protocol.utility.MinecraftReflection.getMinecraftClass;
@@ -588,23 +586,7 @@ public class BukkitConverters {
public static EquivalentConverter<WrappedBlockData> getWrappedBlockDataConverter() {
return ignoreNull(handle(WrappedBlockData::getHandle, WrappedBlockData::fromHandle, WrappedBlockData.class));
}
/**
* Retrieve a converter for wrapped block entity type.
* @return Wrapped block entity type.
*/
public static EquivalentConverter<WrappedRegistrable> getWrappedRegistrable(
@NotNull final Class<?> registrableClass
) {
return ignoreNull(
handle(
WrappedRegistrable::getHandle,
handle -> WrappedRegistrable.fromHandle(registrableClass, handle),
WrappedRegistrable.class
)
);
}
/**
* Retrieve a converter for wrapped attribute snapshots.
* @return Wrapped attribute snapshot converter.
@@ -621,10 +603,6 @@ public class BukkitConverters {
return ignoreNull(handle(WrappedProfileKeyData::getHandle, WrappedProfileKeyData::new, WrappedProfileKeyData.class));
}
public static EquivalentConverter<WrappedRemoteChatSessionData> getWrappedRemoteChatSessionDataConverter() {
return ignoreNull(handle(WrappedRemoteChatSessionData::getHandle, WrappedRemoteChatSessionData::new, WrappedRemoteChatSessionData.class));
}
/**
* @return converter for cryptographic signature data that are used in login and chat packets
*/
@@ -632,13 +610,6 @@ public class BukkitConverters {
return ignoreNull(handle(WrappedSaltedSignature::getHandle, WrappedSaltedSignature::new, WrappedSaltedSignature.class));
}
/**
* @return converter for an encoded cryptographic message signature
*/
public static EquivalentConverter<WrappedMessageSignature> getWrappedMessageSignatureConverter() {
return ignoreNull(handle(WrappedMessageSignature::getHandle, WrappedMessageSignature::new, WrappedMessageSignature.class));
}
public static EquivalentConverter<WrappedLevelChunkData.ChunkData> getWrappedChunkDataConverter() {
return ignoreNull(handle(WrappedLevelChunkData.ChunkData::getHandle, WrappedLevelChunkData.ChunkData::new, WrappedLevelChunkData.ChunkData.class));
}
@@ -647,10 +618,6 @@ public class BukkitConverters {
return ignoreNull(handle(WrappedLevelChunkData.LightData::getHandle, WrappedLevelChunkData.LightData::new, WrappedLevelChunkData.LightData.class));
}
public static EquivalentConverter<PacketContainer> getPacketContainerConverter() {
return ignoreNull(handle(PacketContainer::getHandle, PacketContainer::fromPacket, PacketContainer.class));
}
/**
* Retrieve a converter for watchable objects and the respective wrapper.
* @return A watchable object converter.
@@ -1111,19 +1078,19 @@ public class BukkitConverters {
});
}
static MethodAccessor getSound = null;
static MethodAccessor getSoundEffect = null;
static FieldAccessor soundKey = null;
private static MethodAccessor getSound = null;
private static MethodAccessor getSoundEffect = null;
private static FieldAccessor soundKey = null;
static MethodAccessor getSoundEffectByKey = null;
static MethodAccessor getSoundEffectBySound = null;
static MethodAccessor getSoundByEffect = null;
private static MethodAccessor getSoundEffectByKey = null;
private static MethodAccessor getSoundEffectBySound = null;
private static MethodAccessor getSoundByEffect = null;
static Map<String, Sound> soundIndex = null;
private static Map<String, Sound> soundIndex = null;
public static EquivalentConverter<Sound> getSoundConverter() {
// Try to create sound converter for new versions greater 1.16.4
if (MinecraftVersion.NETHER_UPDATE_4.atOrAbove()) {
try {
if (getSoundEffectByKey == null || getSoundEffectBySound == null || getSoundByEffect == null) {
Class<?> craftSound = MinecraftReflection.getCraftSoundClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, true);
@@ -1131,19 +1098,19 @@ public class BukkitConverters {
getSoundEffectByKey = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
"getSoundEffect",
MinecraftReflection.getSoundEffectClass(),
String.class
new Class<?>[]{String.class}
));
getSoundEffectBySound = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
"getSoundEffect",
MinecraftReflection.getSoundEffectClass(),
Sound.class
new Class<?>[]{Sound.class}
));
getSoundByEffect = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
"getBukkit",
Sound.class,
MinecraftReflection.getSoundEffectClass()
new Class<?>[]{MinecraftReflection.getSoundEffectClass()}
));
}
@@ -1161,17 +1128,10 @@ public class BukkitConverters {
@Override
public Sound getSpecific(Object generic) {
try {
return (Sound) getSoundByEffect.invoke(null, generic);
} catch (IllegalStateException ex) {
if (ex.getCause() instanceof NullPointerException) {
// "null" sounds cause NPEs inside getSoundByEffect
return null;
}
throw ex;
}
return (Sound) getSoundByEffect.invoke(null, generic);
}
});
} catch (Exception e) {
}
// Fall back to sound converter from legacy versions before 1.16.4
@@ -1179,9 +1139,9 @@ public class BukkitConverters {
Class<?> craftSound = MinecraftReflection.getCraftSoundClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, true);
getSound = Accessors.getMethodAccessor(
fuzzy.getMethodByReturnTypeAndParameters("getSound", String.class, Sound.class));
fuzzy.getMethodByReturnTypeAndParameters("getSound", String.class, new Class<?>[]{Sound.class}));
getSoundEffect = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("getSoundEffect",
MinecraftReflection.getSoundEffectClass(), String.class));
MinecraftReflection.getSoundEffectClass(), new Class<?>[]{String.class}));
}
return ignoreNull(new EquivalentConverter<Sound>() {
@@ -14,21 +14,13 @@
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Utility class for converters
@@ -225,104 +217,4 @@ public class Converters {
}
});
}
public static <T> EquivalentConverter<Iterable<T>> iterable(
final EquivalentConverter<T> elementConverter,
final Supplier<List<T>> specificCollectionFactory,
final Supplier<List<?>> genericCollectionFactory
) {
return ignoreNull(new EquivalentConverter<Iterable<T>>() {
@Override
public Object getGeneric(Iterable<T> specific) {
// generics are very cool, thank you java
List<Object> targetCollection = (List<Object>) genericCollectionFactory.get();
for (T element : specific) {
Object generic = elementConverter.getGeneric(element);
if (generic != null) {
targetCollection.add(generic);
}
}
return targetCollection;
}
@Override
public Iterable<T> getSpecific(Object generic) {
if (generic instanceof Iterable<?>) {
Iterable<Object> sourceCollection = (Iterable<Object>) generic;
List<T> targetCollection = specificCollectionFactory.get();
// copy over all elements into a new collection
for (Object element : sourceCollection) {
T specific = elementConverter.getSpecific(element);
if (specific != null) {
targetCollection.add(specific);
}
}
return targetCollection;
}
// not valid
return null;
}
@Override
public Class<Iterable<T>> getSpecificType() {
return (Class) Iterable.class;
}
});
}
private static MethodAccessor holderGetValue;
public static <T> EquivalentConverter<T> holder(final EquivalentConverter<T> converter,
final WrappedRegistry registry) {
return new EquivalentConverter<T>() {
@Override
public Object getGeneric(T specific) {
Object generic = converter.getGeneric(specific);
return registry.getHolder(generic);
}
@Override
public T getSpecific(Object generic) {
if (holderGetValue == null) {
Class<?> holderClass = MinecraftReflection.getHolderClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(holderClass, false);
holderGetValue = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(0)
.banModifier(Modifier.STATIC)
.returnTypeExact(Object.class)
.build()));
}
Object value = holderGetValue.invoke(generic);
return converter.getSpecific(value);
}
@Override
public Class<T> getSpecificType() {
return converter.getSpecificType();
}
};
}
public static <T> List<T> toList(Iterable<? extends T> iterable) {
if (iterable instanceof List) {
return (List<T>) iterable;
}
List<T> result = new ArrayList<>();
if (iterable instanceof Collection) {
Collection<T> coll = (Collection<T>) iterable;
result.addAll(coll);
} else {
for (T elem : iterable) {
result.add(elem);
}
}
return result;
}
}
@@ -65,7 +65,6 @@ public abstract class EnumWrappers {
/**
* @deprecated Replaced by NOT_SET
*/
@Deprecated
NONE;
/**
@@ -378,9 +377,9 @@ public abstract class EnumWrappers {
/**
* Wrapped EntityPose enum for use in Entity Metadata Packet.<br>
* Remember to use {@link #toNms()} when adding to a {@link WrappedDataWatcher}. <br>
* Serializer is obtained using Registry.get(EnumWrappers.getEntityPoseClass())
*
*
* @apiNote Remember to use {@link #toNms()} when adding to a {@link WrappedDataWatcher}. <br>
* Serializer is obtained using Registry.get(EnumWrappers.getEntityPoseClass())
* @since 1.13
* @author Lewys Davies (Lew_)
*/
@@ -30,8 +30,6 @@ import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import javax.annotation.Nullable;
/**
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
* @author dmulloy2
@@ -45,9 +43,6 @@ public class PlayerInfoData {
private final NativeGameMode gameMode;
private final WrappedGameProfile profile;
private final WrappedChatComponent displayName;
@Nullable
private final WrappedRemoteChatSessionData remoteChatSessionData;
@Nullable
private final WrappedProfileKeyData profileKeyData;
// This is the same order as the NMS class, minus the packet (which isn't a field)
@@ -59,35 +54,7 @@ public class PlayerInfoData {
this(profile.getUUID(), latency, true, gameMode, profile, displayName, keyData);
}
/**
* Constructs a new PlayerInfoData for Minecraft 1.19 or later without signature data
* @see PlayerInfoData#PlayerInfoData(UUID, int, boolean, NativeGameMode, WrappedGameProfile, WrappedChatComponent, WrappedRemoteChatSessionData)
*
* @param profileId the id of the profile (has to be non-null)
* @param latency the latency in milliseconds
* @param listed whether the player is listed in the tab list
* @param gameMode the game mode
* @param profile the game profile
* @param displayName display name in tab list (optional)
*/
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName) {
this(profileId, latency, listed, gameMode, profile, displayName, (WrappedRemoteChatSessionData) null);
}
/**
* Constructs a new PlayerInfoData for Minecraft 1.19. This is incompatible on 1.19.3.
* @see PlayerInfoData#PlayerInfoData(UUID, int, boolean, NativeGameMode, WrappedGameProfile, WrappedChatComponent, WrappedRemoteChatSessionData)
*
* @param profileId the id of the profile (has to be non-null)
* @param latency the latency in milliseconds
* @param listed whether the player is listed in the tab list
* @param gameMode the game mode
* @param profile the game profile
* @param displayName display name in tab list (optional)
* @param profileKeyData the public key for the profile or null
*/
@Deprecated
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedProfileKeyData profileKeyData) {
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, WrappedProfileKeyData profileKeyData) {
this.profileId = profileId;
this.latency = latency;
this.listed = listed;
@@ -95,29 +62,6 @@ public class PlayerInfoData {
this.profile = profile;
this.displayName = displayName;
this.profileKeyData = profileKeyData;
this.remoteChatSessionData = null;
}
/**
* Constructs a new PlayerInfoData for Minecraft 1.19.3 or later.
*
* @param profileId the id of the profile (has to be non-null)
* @param latency the latency in milliseconds
* @param listed whether the player is listed in the tab list
* @param gameMode the game mode
* @param profile the game profile
* @param displayName display name in tab list (optional)
* @param remoteChatSession the remote chat session for this profile or null
*/
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
this.profileId = profileId;
this.latency = latency;
this.listed = listed;
this.gameMode = gameMode;
this.profile = profile;
this.displayName = displayName;
this.profileKeyData = null;
this.remoteChatSessionData = remoteChatSession;
}
/**
@@ -125,9 +69,6 @@ public class PlayerInfoData {
* @return the id of the profile
*/
public UUID getProfileId() {
if(profileId == null && profile != null) {
return profile.getUUID(); // Ensure forward compatability
}
return profileId;
}
@@ -156,7 +97,7 @@ public class PlayerInfoData {
}
/**
* Gets if the player is listed on the client (since 1.19.3)
* Gets if the player is listed on the client.
* @return if the player is listed
*/
public boolean isListed() {
@@ -180,21 +121,11 @@ public class PlayerInfoData {
}
/**
* Returns the public key of the profile (since 1.19). Returns the public key of the remote chat session since 1.19.3
* @return The public key of the profile.
* Gets the profile key data of the player represented by this data, null if not present.
* @return The profile key data
*/
@Nullable
public WrappedProfileKeyData getProfileKeyData() {
return this.profileKeyData != null ? this.profileKeyData : (this.remoteChatSessionData != null ? this.remoteChatSessionData.getProfilePublicKey() : null);
}
/**
* Returns the remoteChatSessionData (since 1.19.3)
* @return The remote chat sesion data or null
*/
@Nullable
public WrappedRemoteChatSessionData getRemoteChatSessionData() {
return this.remoteChatSessionData;
return this.profileKeyData;
}
/**
@@ -226,7 +157,8 @@ public class PlayerInfoData {
args.add(MinecraftReflection.getIChatBaseComponentClass());
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
args.add(MinecraftReflection.getRemoteChatSessionDataClass());
// RemoteChatSession$a...
args.add(MinecraftReflection.getRemoteChatSessionClass().getClasses()[0]);
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
args.add(MinecraftReflection.getProfilePublicKeyDataClass());
}
@@ -243,28 +175,26 @@ public class PlayerInfoData {
Object gameMode = EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode);
Object displayName = specific.displayName != null ? specific.displayName.handle : null;
Object profile = specific.profile != null ? specific.profile.handle : null;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
return constructor.newInstance(
specific.profileId,
profile,
specific.profile.handle,
specific.listed,
specific.latency,
gameMode,
displayName,
specific.remoteChatSessionData != null ? BukkitConverters.getWrappedRemoteChatSessionDataConverter().getGeneric(specific.remoteChatSessionData) : null
);
null); // TODO: do we want to support this?
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
return constructor.newInstance(
profile,
specific.profile.handle,
specific.latency,
gameMode,
displayName,
specific.profileKeyData == null ? null : specific.profileKeyData.handle);
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
return constructor.newInstance(profile, specific.latency, gameMode, displayName);
return constructor.newInstance(specific.profile.handle, specific.latency, gameMode, displayName);
} else {
return constructor.newInstance(null, profile, specific.latency, gameMode, displayName);
return constructor.newInstance(null, specific.profile.handle, specific.latency, gameMode, displayName);
}
} catch (Exception e) {
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
@@ -292,18 +222,8 @@ public class PlayerInfoData {
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
WrappedChatComponent displayName = displayNames.read(0);
if(MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
return new PlayerInfoData(modifier.<UUID>withType(UUID.class).read(0),
latency,
modifier.<Boolean>withType(boolean.class).read(0),
gameMode,
gameProfile,
displayName,
modifier.withType(MinecraftReflection.getRemoteChatSessionDataClass(), BukkitConverters.getWrappedRemoteChatSessionDataConverter()).read(0)
);
}
WrappedProfileKeyData key = null;
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
if (MinecraftVersion.WILD_UPDATE.atOrAbove() && !MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
StructureModifier<WrappedProfileKeyData> keyData = modifier.withType(
MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter());
key = keyData.read(0);
@@ -333,34 +253,20 @@ public class PlayerInfoData {
// Only compare objects of similar type
if (obj instanceof PlayerInfoData) {
PlayerInfoData other = (PlayerInfoData) obj;
return Objects.equals(profile, other.profile)
&& Objects.equals(profileId, other.profileId)
&& latency == other.latency
&& gameMode == other.gameMode
&& Objects.equals(displayName, other.displayName)
&& listed == other.listed
&& Objects.equals(remoteChatSessionData, other.remoteChatSessionData)
&& Objects.equals(profileKeyData, other.profileKeyData);
return profile.equals(other.profile) && latency == other.latency && gameMode == other.gameMode
&& Objects.equals(displayName, other.displayName);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(latency, gameMode, profile, displayName, profileKeyData, remoteChatSessionData, listed);
return Objects.hash(latency, gameMode, profile, displayName);
}
@Override
public String toString() {
if(MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
return String.format("PlayerInfoData[latency=%s, listed=%b, gameMode=%s, profile=%s, displayName=%s, remoteChatSession=%s]",
latency, listed, gameMode, profile, displayName, remoteChatSessionData);
}
if(MinecraftVersion.WILD_UPDATE.atOrAbove()) {
return String.format("PlayerInfoData[latency=%s, listed=%b, gameMode=%s, profile=%s, displayName=%s, profilePublicKey=%s]",
latency, listed, gameMode, profile, displayName, profileKeyData);
}
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s]",
latency, gameMode, profile, displayName);
latency, gameMode, profile, displayName);
}
}
@@ -4,7 +4,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
@@ -143,11 +142,17 @@ public class TroveWrapper {
private static Object getDecorated(@Nonnull Object trove) {
if (trove == null)
throw new IllegalArgumentException("trove instance cannot be non-null.");
AbstractFuzzyMatcher<Class<?>> match = FuzzyMatchers.matchSuper(trove.getClass());
Class<?> decorators = getClassSource(trove.getClass()).loadClass("TDecorator")
.orElseThrow(() -> new IllegalStateException("TDecorators class not found"));
Class<?> decorators = null;
try {
// Attempt to get decorator class
decorators = getClassSource(trove.getClass()).loadClass("TDecorators");
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
// Find an appropriate wrapper method in TDecorators
for (Method method : decorators.getMethods()) {
Class<?>[] types = method.getParameterTypes();
@@ -168,7 +168,6 @@ public class WrappedAttribute extends AbstractWrapper {
* @deprecated Removed in 1.17
*/
@Nullable
@Deprecated
public PacketContainer getParentPacket() {
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
return null;
@@ -22,7 +22,7 @@ import com.google.common.base.Preconditions;
* @author Kristian
*/
public class WrappedAttributeModifier extends AbstractWrapper {
private static final boolean OPERATION_ENUM = MinecraftVersion.VILLAGE_UPDATE.atOrAbove();
private static final boolean OPERATION_ENUM = MinecraftVersion.atOrAbove(MinecraftVersion.VILLAGE_UPDATE);
private static final Class<?> OPERATION_CLASS;
private static final EquivalentConverter<Operation> OPERATION_CONVERTER;
@@ -12,7 +12,7 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
*/
public class WrappedDataValue extends AbstractWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getNullableNMS("network.syncher.DataWatcher$b", "network.syncher.SynchedEntityData$DataValue");
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getMinecraftClass("network.syncher.DataWatcher$b");
private static ConstructorAccessor constructor;
@@ -941,8 +941,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/**
* Gets the first serializer associated with a given class and optional state.
* <br/>
* <b>Note</b>: If the serializer is optional, values <i>must</i> be
*
* <p><b>Note</b>: If the serializer is optional, values <i>must<i> be
* wrapped in an {@link Optional}
*
* @param clazz Class to find serializer for
@@ -16,7 +16,6 @@ import com.google.common.base.Preconditions;
* @author Kristian
* @deprecated IntHashMap no longer exists
*/
@Deprecated
public class WrappedIntHashMap extends AbstractWrapper {
private static Method PUT_METHOD;
private static Method GET_METHOD;
@@ -1,48 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* @author Lukas Alt
* @since 24.04.2023
*/
public class WrappedMessageSignature extends AbstractWrapper {
private final static Class<?> HANDLE_TYPE = MinecraftReflection.getMessageSignatureClass();
private static ConstructorAccessor CONSTRUCTOR;
private StructureModifier<Object> modifier;
/**
* Construct a new NMS wrapper.
*
* @param handle - the NMS handle
*/
public WrappedMessageSignature(Object handle) {
super(HANDLE_TYPE);
this.setHandle(handle);
}
public WrappedMessageSignature(byte[] bytes) {
super(HANDLE_TYPE);
if(CONSTRUCTOR == null) {
CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE, byte[].class);
}
this.setHandle(CONSTRUCTOR.invoke(bytes));
}
@Override
protected void setHandle(Object handle) {
super.setHandle(handle);
this.modifier = new StructureModifier<>(HANDLE_TYPE).withTarget(handle);
}
public byte[] getBytes() {
return modifier.<byte[]>withType(byte[].class).read(0);
}
public void setBytes(byte[] bytes) {
modifier.<byte[]>withType(byte[].class).write(0, bytes);
}
}
@@ -1,185 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
* <p>
* This program 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 2 of
* the License, or (at your option) any later version.
* <p>
* This program 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.
* <p>
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Represents a wrapper around registrable objects.
*
* @author portlek
*/
public final class WrappedRegistrable extends AbstractWrapper implements ClonableWrapper {
@NotNull
private final Factory factory;
private WrappedRegistrable(
@NotNull final Factory factory,
@NotNull final Object handle
) {
super(factory.registrableClass);
this.factory = factory;
setHandle(handle);
}
@NotNull
public static WrappedRegistrable fromHandle(
@NotNull final Factory factory,
@NotNull final Object handle
) {
return new WrappedRegistrable(factory, handle);
}
@NotNull
public static WrappedRegistrable fromHandle(
@NotNull final Class<?> registrableClass,
@NotNull final Object handle
) {
return fromHandle(Factory.getOrCreate(registrableClass), handle);
}
@NotNull
public static WrappedRegistrable fromClassAndKey(
@NotNull final Class<?> registrableClass,
@NotNull final MinecraftKey key
) {
final Factory factory = Factory.getOrCreate(registrableClass);
return fromHandle(factory, factory.getHandle(key));
}
@NotNull
public static WrappedRegistrable fromClassAndKey(
@NotNull final Class<?> registrableClass,
@NotNull final String key
) {
return fromClassAndKey(registrableClass, new MinecraftKey(key));
}
@NotNull
public static WrappedRegistrable blockEntityType(
@NotNull final MinecraftKey key
) {
return fromClassAndKey(MinecraftReflection.getBlockEntityTypeClass(), key);
}
@NotNull
public static WrappedRegistrable blockEntityType(
@NotNull final String key
) {
return blockEntityType(new MinecraftKey(key));
}
/**
* Gets this registrable object's Minecraft key
*
* @return The Minecraft key
*/
public MinecraftKey getKey() {
return factory.getKey(handle);
}
/**
* Sets this registrable object's Minecraft key
*
* @param key Minecraft key
*/
public void setKey(MinecraftKey key) {
setHandle(factory.getHandle(key));
}
public WrappedRegistrable deepClone() {
return fromHandle(factory, handle);
}
@Override
public String toString() {
return "WrappedRegistrable[handle=" + handle + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getKey().hashCode();
return result;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof WrappedRegistrable)) {
return false;
}
final WrappedRegistrable that = (WrappedRegistrable) o;
return handle.equals(that.handle) || (getKey() == that.getKey());
}
private static final class Factory {
private static final Map<Class<?>, Factory> CACHE = new ConcurrentHashMap<>();
@NotNull
private final Class<?> registrableClass;
@NotNull
private final WrappedRegistry registry;
@NotNull
private final FieldAccessor fieldAccessor;
private Factory(@NotNull final Class<?> registrableClass) {
this.registrableClass = registrableClass;
this.registry = WrappedRegistry.getRegistry(registrableClass);
this.fieldAccessor = Accessors.getFieldAccessor(
FuzzyReflection.fromClass(registrableClass, true)
.getField(
FuzzyFieldContract.newBuilder()
.typeExact(registrableClass)
.build()
)
);
}
@NotNull
public static Factory getOrCreate(
@NotNull final Class<?> registrableClass
) {
return CACHE.computeIfAbsent(registrableClass, Factory::new);
}
@NotNull
public MinecraftKey getKey(@NotNull final Object handle) {
return registry.getKey(fieldAccessor.get(handle));
}
@NotNull
public Object getHandle(MinecraftKey key) {
return registry.get(key);
}
}
}
@@ -5,7 +5,6 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Field;
@@ -25,8 +24,6 @@ public class WrappedRegistry {
private static final MethodAccessor GET_ID;
private static final MethodAccessor GET_KEY;
private static final MethodAccessor GET_HOLDER;
static {
Map<Class<?>, WrappedRegistry> regMap = new HashMap<>();
@@ -103,23 +100,6 @@ public class WrappedRegistry {
.parameterCount(1)
.returnTypeExact(MinecraftReflection.getMinecraftKeyClass())
.build()));
MethodAccessor getHolder = null;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
try {
getHolder = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.banModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getHolderClass())
.requireModifier(Modifier.PUBLIC)
.build()));
} catch (IllegalArgumentException ignored) {
}
}
GET_HOLDER = getHolder;
}
private final Object handle;
@@ -152,10 +132,6 @@ public class WrappedRegistry {
return (int) GET_ID.invoke(this.handle, entry);
}
public Object getHolder(Object generic) {
return GET_HOLDER.invoke(handle, generic);
}
public static WrappedRegistry getAttributeRegistry() {
return getRegistry(MinecraftReflection.getAttributeBase());
}
@@ -164,10 +140,6 @@ public class WrappedRegistry {
return getRegistry(MinecraftReflection.getDimensionManager());
}
public static WrappedRegistry getSoundRegistry() {
return getRegistry(MinecraftReflection.getSoundEffectClass());
}
public static WrappedRegistry getRegistry(Class<?> type) {
return REGISTRY.get(type);
}
@@ -1,96 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.util.UUID;
/**
* A wrapper around the remote chat session.
*
* @since 1.19.3
*/
public class WrappedRemoteChatSessionData extends AbstractWrapper {
private final static Class<?> HANDLE_TYPE = MinecraftReflection.getRemoteChatSessionDataClass();
private static ConstructorAccessor CONSTRUCTOR;
private StructureModifier<Object> modifier;
/**
* Constructs a new profile public key wrapper directly from a nms RemoteChatSession.Data/RemoteChatSession.a object.
*
* @param handle the handle to create the wrapper from.
*/
public WrappedRemoteChatSessionData(Object handle) {
super(HANDLE_TYPE);
this.setHandle(handle);
}
public WrappedRemoteChatSessionData(UUID sessionId, WrappedProfilePublicKey.WrappedProfileKeyData profilePublicKey) {
super(HANDLE_TYPE);
if (CONSTRUCTOR == null) {
CONSTRUCTOR = Accessors.getConstructorAccessor(
this.getHandleType(),
UUID.class, MinecraftReflection.getProfilePublicKeyDataClass());
}
this.setHandle(CONSTRUCTOR.invoke(sessionId, profilePublicKey.getHandle()));
}
/**
* Retrieves the id of the current session
* @return session id
*/
public UUID getSessionID() {
return this.modifier.<UUID>withType(UUID.class).read(0);
}
/**
* Set the id for this session
* @param sessionId new session id
*/
public void setSessionID(UUID sessionId) {
this.modifier.<UUID>withType(UUID.class).write(0, sessionId);
}
/**
* Retrieves the ProfileKeyData
* @return the public key data for this session
*/
public WrappedProfilePublicKey.WrappedProfileKeyData getProfilePublicKey() {
return this.modifier.withType(MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter()).read(0);
}
/**
* Set the profile key data for this session
* @param data ProfileKeyData
*/
public void setProfilePublicKey(WrappedProfilePublicKey.WrappedProfileKeyData data) {
this.modifier.withType(MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter()).write(0, data);
}
@Override
protected void setHandle(Object handle) {
super.setHandle(handle);
this.modifier = new StructureModifier<>(HANDLE_TYPE).withTarget(handle);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof WrappedRemoteChatSessionData) {
return handle.equals(((WrappedRemoteChatSessionData) obj).getHandle());
}
return false;
}
@Override
public String toString() {
return handle.toString();
}
@Override
public int hashCode() {
return handle.hashCode();
}
}
@@ -1,13 +1,16 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.ping.LegacyServerPing;
import com.comphenix.protocol.wrappers.ping.ServerPingImpl;
import com.comphenix.protocol.wrappers.ping.ServerPingRecord;
import com.comphenix.protocol.utility.Util;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
@@ -15,6 +18,8 @@ import com.google.common.io.ByteStreams;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import javax.imageio.ImageIO;
@@ -32,14 +37,52 @@ import java.util.List;
* Represents a server ping packet data.
* @author Kristian
*/
public class WrappedServerPing implements ClonableWrapper {
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
public class WrappedServerPing extends AbstractWrapper implements ClonableWrapper {
private static Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
private static Class<?> GAME_PROFILE_ARRAY = MinecraftReflection.getArrayClass(GAME_PROFILE);
// Server ping fields
private static Class<?> SERVER_PING = MinecraftReflection.getServerPingClass();
private static ConstructorAccessor SERVER_PING_CONSTRUCTOR = Accessors.getConstructorAccessor(SERVER_PING);
private static FieldAccessor DESCRIPTION = Accessors.getFieldAccessor(SERVER_PING, MinecraftReflection.getIChatBaseComponentClass(), true);
private static FieldAccessor PLAYERS = Accessors.getFieldAccessor(SERVER_PING, MinecraftReflection.getServerPingPlayerSampleClass(), true);
private static FieldAccessor VERSION = Accessors.getFieldAccessor(SERVER_PING, MinecraftReflection.getServerPingServerDataClass(), true);
private static FieldAccessor FAVICON = Accessors.getFieldAccessor(SERVER_PING, String.class, true);
private static FieldAccessor[] BOOLEAN_ACCESSORS = Accessors.getFieldAccessorArray(SERVER_PING, boolean.class, true);
// For converting to the underlying array
private static EquivalentConverter<Iterable<? extends WrappedGameProfile>> PROFILE_CONVERT =
BukkitConverters.getArrayConverter(GAME_PROFILE, BukkitConverters.getWrappedGameProfileConverter());
// Server ping player sample fields
private static Class<?> PLAYERS_CLASS = MinecraftReflection.getServerPingPlayerSampleClass();
private static ConstructorAccessor PLAYERS_CONSTRUCTOR = Accessors.getConstructorAccessor(PLAYERS_CLASS, int.class, int.class);
private static FieldAccessor[] PLAYERS_INTS = Accessors.getFieldAccessorArray(PLAYERS_CLASS, int.class, true);
private static FieldAccessor PLAYERS_PROFILES = Accessors.getFieldAccessor(PLAYERS_CLASS, GAME_PROFILE_ARRAY, true);
private static FieldAccessor PLAYERS_MAXIMUM = PLAYERS_INTS[0];
private static FieldAccessor PLAYERS_ONLINE = PLAYERS_INTS[1];
// Server ping serialization
private static Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
private static MethodAccessor GSON_TO_JSON = Accessors.getMethodAccessor(GSON_CLASS, "toJson", Object.class);
private static MethodAccessor GSON_FROM_JSON = Accessors.getMethodAccessor(GSON_CLASS, "fromJson", String.class, Class.class);
private static FieldAccessor PING_GSON = Accessors.getMemorizing(Accessors.getFieldAccessor(
PacketType.Status.Server.SERVER_INFO.getPacketClass(), GSON_CLASS, true
));
// Server data fields
private static Class<?> VERSION_CLASS = MinecraftReflection.getServerPingServerDataClass();
private static ConstructorAccessor VERSION_CONSTRUCTOR = Accessors.getConstructorAccessor(VERSION_CLASS, String.class, int.class);
private static FieldAccessor VERSION_NAME = Accessors.getFieldAccessor(VERSION_CLASS, String.class, true);
private static FieldAccessor VERSION_PROTOCOL = Accessors.getFieldAccessor(VERSION_CLASS, int.class, true);
// Get profile from player
private static final FieldAccessor ENTITY_HUMAN_PROFILE = Accessors.getFieldAccessor(
MinecraftReflection.getEntityPlayerClass().getSuperclass(), GAME_PROFILE, true);
private static FieldAccessor ENTITY_HUMAN_PROFILE = Accessors.getFieldAccessor(
MinecraftReflection.getEntityPlayerClass().getSuperclass(), GAME_PROFILE, true);
private final ServerPingImpl impl;
// Inner class
private Object players; // may be NULL
private Object version;
/**
* Construct a new server ping initialized with a zero player count, and zero maximum.
@@ -47,41 +90,34 @@ public class WrappedServerPing implements ClonableWrapper {
* Note that the version string is set to 1.9.4.
*/
public WrappedServerPing() {
this.impl = newImpl();
super(MinecraftReflection.getServerPingClass());
setHandle(SERVER_PING_CONSTRUCTOR.invoke());
resetPlayers();
resetVersion();
}
private WrappedServerPing(Object handle) {
this.impl = newImpl(handle);
}
private static ServerPingImpl newImpl() {
if (MinecraftVersion.FEATURE_PREVIEW_2.atOrAbove()) {
return new ServerPingRecord();
}
return new LegacyServerPing();
}
private static ServerPingImpl newImpl(Object handle) {
if (MinecraftVersion.FEATURE_PREVIEW_2.atOrAbove()) {
return new ServerPingRecord(handle);
}
return new LegacyServerPing(handle);
super(MinecraftReflection.getServerPingClass());
setHandle(handle);
this.players = PLAYERS.get(handle);
this.version = VERSION.get(handle);
}
/**
* Set the player count and player maximum to the default values.
*/
private void resetPlayers() {
impl.resetPlayers();
protected void resetPlayers() {
players = PLAYERS_CONSTRUCTOR.invoke(0, 0);
PLAYERS.set(handle, players);
}
/**
* Reset the version string to the default state.
*/
private void resetVersion() {
impl.resetVersion();
protected void resetVersion() {
MinecraftVersion minecraftVersion = MinecraftVersion.getCurrentVersion();
version = VERSION_CONSTRUCTOR.invoke(minecraftVersion.toString(), MinecraftProtocolVersion.getCurrentVersion());
VERSION.set(handle, version);
}
/**
@@ -99,18 +135,15 @@ public class WrappedServerPing implements ClonableWrapper {
* @return The wrapped server ping.
*/
public static WrappedServerPing fromJson(String json) {
if(MinecraftVersion.FEATURE_PREVIEW_2.atOrAbove()) {
return new WrappedServerPing(ServerPingRecord.fromJson(json).getHandle());
}
return new WrappedServerPing(LegacyServerPing.fromJson(json));
return fromHandle(GSON_FROM_JSON.invoke(PING_GSON.get(null), json, SERVER_PING));
}
/**
* Retrieve the message of the day.
* @return The message of the day.
* @return The messge of the day.
*/
public WrappedChatComponent getMotD() {
return impl.getMotD();
return WrappedChatComponent.fromHandle(DESCRIPTION.get(handle));
}
/**
@@ -118,7 +151,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @param description - message of the day.
*/
public void setMotD(WrappedChatComponent description) {
impl.setMotD(description);
DESCRIPTION.set(handle, description.getHandle());
}
/**
@@ -134,7 +167,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @return The favicon, or NULL if no favicon will be displayed.
*/
public CompressedImage getFavicon() {
String favicon = impl.getFavicon();
String favicon = (String) FAVICON.get(handle);
return (favicon != null) ? CompressedImage.fromEncodedText(favicon) : null;
}
@@ -143,7 +176,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @param image - the new compressed image or NULL if no favicon should be displayed.
*/
public void setFavicon(CompressedImage image) {
impl.setFavicon((image != null) ? image.toEncodedText() : null);
FAVICON.set(handle, (image != null) ? image.toEncodedText() : null);
}
/**
@@ -154,7 +187,7 @@ public class WrappedServerPing implements ClonableWrapper {
*/
@Deprecated
public boolean isChatPreviewEnabled() {
return impl.isChatPreviewEnabled();
return (Boolean) BOOLEAN_ACCESSORS[0].get(handle);
}
/**
@@ -165,7 +198,7 @@ public class WrappedServerPing implements ClonableWrapper {
*/
@Deprecated
public void setChatPreviewEnabled(boolean chatPreviewEnabled) {
impl.setChatPreviewEnabled(chatPreviewEnabled);
BOOLEAN_ACCESSORS[0].set(handle, chatPreviewEnabled);
}
/**
@@ -174,7 +207,8 @@ public class WrappedServerPing implements ClonableWrapper {
* @since 1.19.1
*/
public boolean isEnforceSecureChat() {
return impl.isEnforceSecureChat();
int index = MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove() ? 0 : 1;
return (Boolean) BOOLEAN_ACCESSORS[index].get(handle);
}
/**
@@ -183,7 +217,8 @@ public class WrappedServerPing implements ClonableWrapper {
* @since 1.19.1
*/
public void setEnforceSecureChat(boolean enforceSecureChat) {
impl.setEnforceSecureChat(enforceSecureChat);
int index = MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove() ? 0 : 1;
BOOLEAN_ACCESSORS[index].set(handle, enforceSecureChat);
}
/**
@@ -193,7 +228,9 @@ public class WrappedServerPing implements ClonableWrapper {
* @see #setPlayersOnline(int)
*/
public int getPlayersOnline() {
return impl.getPlayersOnline();
if (players == null)
throw new IllegalStateException("The player count has been hidden.");
return (Integer) PLAYERS_ONLINE.get(players);
}
/**
@@ -204,7 +241,9 @@ public class WrappedServerPing implements ClonableWrapper {
* @param online - online players.
*/
public void setPlayersOnline(int online) {
impl.setPlayersOnline(online);
if (players == null)
resetPlayers();
PLAYERS_ONLINE.set(players, online);
}
/**
@@ -214,7 +253,9 @@ public class WrappedServerPing implements ClonableWrapper {
* @see #setPlayersMaximum(int)
*/
public int getPlayersMaximum() {
return impl.getPlayersMaximum();
if (players == null)
throw new IllegalStateException("The player maximum has been hidden.");
return (Integer) PLAYERS_MAXIMUM.get(players);
}
/**
@@ -225,7 +266,9 @@ public class WrappedServerPing implements ClonableWrapper {
* @param maximum - maximum player count.
*/
public void setPlayersMaximum(int maximum) {
impl.setPlayersMaximum(maximum);
if (players == null)
resetPlayers();
PLAYERS_MAXIMUM.set(players, maximum);
}
/**
@@ -236,7 +279,14 @@ public class WrappedServerPing implements ClonableWrapper {
*/
public void setPlayersVisible(boolean visible) {
if (isPlayersVisible() != visible) {
impl.setPlayersVisible(visible);
if (visible) {
// Recreate the count and maximum
Server server = Bukkit.getServer();
setPlayersMaximum(server.getMaxPlayers());
setPlayersOnline(Bukkit.getOnlinePlayers().size());
} else {
PLAYERS.set(handle, players = null);
}
}
}
@@ -247,7 +297,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @return TRUE if the player statistics is visible, FALSE otherwise.
*/
public boolean isPlayersVisible() {
return impl.arePlayersVisible();
return players != null;
}
/**
@@ -255,7 +305,12 @@ public class WrappedServerPing implements ClonableWrapper {
* @return Logged in players or an empty list if no player names will be displayed.
*/
public ImmutableList<WrappedGameProfile> getPlayers() {
return impl.getPlayers();
if (players == null)
return ImmutableList.of();
Object playerProfiles = PLAYERS_PROFILES.get(players);
if (playerProfiles == null)
return ImmutableList.of();
return ImmutableList.copyOf(PROFILE_CONVERT.getSpecific(playerProfiles));
}
/**
@@ -263,9 +318,9 @@ public class WrappedServerPing implements ClonableWrapper {
* @param profile - every logged in player.
*/
public void setPlayers(Iterable<? extends WrappedGameProfile> profile) {
if (!isPlayersVisible())
if (players == null)
resetPlayers();
impl.setPlayers(profile);
PLAYERS_PROFILES.set(players, (profile != null) ? PROFILE_CONVERT.getGeneric(profile) : null);
}
/**
@@ -288,7 +343,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @return The version name.
*/
public String getVersionName() {
return impl.getVersionName();
return (String) VERSION_NAME.get(version);
}
/**
@@ -296,7 +351,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @param name - the new version name.
*/
public void setVersionName(String name) {
impl.setVersionName(name);
VERSION_NAME.set(version, name);
}
/**
@@ -304,7 +359,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @return The protocol.
*/
public int getVersionProtocol() {
return impl.getVersionProtocol();
return (Integer) VERSION_PROTOCOL.get(version);
}
/**
@@ -312,12 +367,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @param protocol - the protocol number.
*/
public void setVersionProtocol(int protocol) {
impl.setVersionProtocol(protocol);
}
@Override
public Object getHandle() {
return impl.getHandle();
VERSION_PROTOCOL.set(version, protocol);
}
/**
@@ -348,7 +398,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @return The JSON representation.
*/
public String toJson() {
return impl.getJson();
return (String) GSON_TO_JSON.invoke(PING_GSON.get(null), handle);
}
@Override
@@ -543,17 +593,4 @@ public class WrappedServerPing implements ClonableWrapper {
return encoded;
}
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof WrappedServerPing)) {
return false;
}
return getHandle().equals(((WrappedServerPing) obj).getHandle());
}
@Override
public int hashCode() {
return getHandle().hashCode();
}
}
@@ -1,31 +0,0 @@
package com.comphenix.protocol.wrappers.codecs;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
public class WrappedCodec extends AbstractWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getCodecClass();
private static final Class<?> ENCODER_CLASS = MinecraftReflection.getLibraryClass("com.mojang.serialization.Encoder");
private static final Class<?> DECODER_CLASS = MinecraftReflection.getLibraryClass("com.mojang.serialization.Decoder");
private static final MethodAccessor ENCODE_START_ACCESSOR = Accessors.getMethodAccessor(ENCODER_CLASS, "encodeStart", MinecraftReflection.getDynamicOpsClass(), Object.class);
private static final MethodAccessor PARSE_ACCESSOR = Accessors.getMethodAccessor(DECODER_CLASS, "parse", MinecraftReflection.getDynamicOpsClass(), Object.class);
private WrappedCodec(Object handle) {
super(HANDLE_TYPE);
this.setHandle(handle);
}
public static WrappedCodec fromHandle(Object handle) {
return new WrappedCodec(handle);
}
public WrappedDataResult encode(Object object, WrappedDynamicOps ops) {
return WrappedDataResult.fromHandle(ENCODE_START_ACCESSOR.invoke(handle, ops.getHandle(), object));
}
public WrappedDataResult parse(Object value, WrappedDynamicOps ops) {
return WrappedDataResult.fromHandle(PARSE_ACCESSOR.invoke(handle, ops.getHandle(), value));
}
}
@@ -1,50 +0,0 @@
package com.comphenix.protocol.wrappers.codecs;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.Function;
public class WrappedDataResult extends AbstractWrapper {
private final static Class<?> HANDLE_TYPE = MinecraftReflection.getLibraryClass("com.mojang.serialization.DataResult");
private final static Class<?> PARTIAL_DATA_RESULT_CLASS = MinecraftReflection.getLibraryClass("com.mojang.serialization.DataResult$PartialResult");
private final static MethodAccessor ERROR_ACCESSOR = Accessors.getMethodAccessor(HANDLE_TYPE, "error");
private final static MethodAccessor RESULT_ACCESSOR = Accessors.getMethodAccessor(HANDLE_TYPE, "result");
private final static MethodAccessor PARTIAL_RESULT_MESSAGE_ACCESSOR = Accessors.getMethodAccessor(PARTIAL_DATA_RESULT_CLASS, "message");
/**
* Construct a new NMS wrapper.
**/
public WrappedDataResult(Object handle) {
super(HANDLE_TYPE);
this.setHandle(handle);
}
public static WrappedDataResult fromHandle(Object handle) {
return new WrappedDataResult(handle);
}
public Optional<Object> getResult() {
return (Optional) RESULT_ACCESSOR.invoke(this.handle);
}
public Optional<String> getErrorMessage() {
return (Optional) ERROR_ACCESSOR.invoke(this.handle);
}
public Object getOrThrow(Function<String, Throwable> errorHandler) {
Optional<String> err = getErrorMessage();
if(err.isPresent()) {
return errorHandler.apply((String) PARTIAL_RESULT_MESSAGE_ACCESSOR.invoke(err.get()));
}
Optional<Object> result = getResult();
if(result.isPresent()) {
return result.get();
}
throw new NoSuchElementException();
}
}
@@ -1,26 +0,0 @@
package com.comphenix.protocol.wrappers.codecs;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
public class WrappedDynamicOps extends AbstractWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDynamicOpsClass();
public static final FieldAccessor NBT_ACCESSOR = Accessors.getFieldAccessor(MinecraftReflection.getNbtOpsClass(), MinecraftReflection.getNbtOpsClass(), false);
public static final FieldAccessor[] JSON_ACCESSORS = Accessors.getFieldAccessorArray(MinecraftReflection.getJsonOpsClass(), MinecraftReflection.getJsonOpsClass(), false);
private WrappedDynamicOps(Object handle) {
super(HANDLE_TYPE);
this.setHandle(handle);
}
public static WrappedDynamicOps fromHandle(Object handle) {
return new WrappedDynamicOps(handle);
}
public static WrappedDynamicOps json(boolean compressed) {
return fromHandle(JSON_ACCESSORS[compressed ? 1 : 0].get(null));
}
public static WrappedDynamicOps nbt() {
return fromHandle(NBT_ACCESSOR);
}
}
@@ -136,10 +136,10 @@ public abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverte
/**
* Returns a string representation of this map. The string representation
* consists of a list of key-value mappings in the order returned by the
* map's <code>entrySet</code> view's iterator, enclosed in braces
* (<code>"{}"</code>). Adjacent mappings are separated by the characters
* <code>", "</code> (comma and space). Each key-value mapping is rendered as
* the key followed by an equals sign (<code>"="</code>) followed by the
* map's <tt>entrySet</tt> view's iterator, enclosed in braces
* (<tt>"{}"</tt>). Adjacent mappings are separated by the characters
* <tt>", "</tt> (comma and space). Each key-value mapping is rendered as
* the key followed by an equals sign (<tt>"="</tt>) followed by the
* associated value. Keys and values are converted to strings as by
* {@link String#valueOf(Object)}.
*
@@ -204,10 +204,10 @@ public abstract class ConvertedMultimap<Key, VInner, VOuter> extends AbstractCon
/**
* Returns a string representation of this map. The string representation
* consists of a list of key-value mappings in the order returned by the
* map's <code>entrySet</code> view's iterator, enclosed in braces
* (<code>"{}"</code>). Adjacent mappings are separated by the characters
* <code>", "</code> (comma and space). Each key-value mapping is rendered as
* the key followed by an equals sign (<code>"="</code>) followed by the
* map's <tt>entrySet</tt> view's iterator, enclosed in braces
* (<tt>"{}"</tt>). Adjacent mappings are separated by the characters
* <tt>", "</tt> (comma and space). Each key-value mapping is rendered as
* the key followed by an equals sign (<tt>"="</tt>) followed by the
* associated value. Keys and values are converted to strings as by
* {@link String#valueOf(Object)}.
*
@@ -1,402 +0,0 @@
package com.comphenix.protocol.wrappers.ping;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.AbstractWrapper;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.google.common.collect.ImmutableList;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a server ping packet data.
* @author Kristian
*/
public final class LegacyServerPing extends AbstractWrapper implements ServerPingImpl {
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
// For converting to the underlying array
private static final EquivalentConverter<Iterable<? extends WrappedGameProfile>> PROFILE_CONVERT =
BukkitConverters.getArrayConverter(GAME_PROFILE, BukkitConverters.getWrappedGameProfileConverter());
// Get profile from player
private static final FieldAccessor ENTITY_HUMAN_PROFILE = Accessors.getFieldAccessor(
MinecraftReflection.getEntityPlayerClass().getSuperclass(), GAME_PROFILE, true);
private static final Class<?> GAME_PROFILE_ARRAY = MinecraftReflection.getArrayClass(GAME_PROFILE);
// Server ping fields
private static final Class<?> SERVER_PING = MinecraftReflection.getServerPingClass();
private static final ConstructorAccessor SERVER_PING_CONSTRUCTOR = Accessors.getConstructorAccessor(SERVER_PING);
private static final FieldAccessor DESCRIPTION = Accessors.getFieldAccessor(SERVER_PING, MinecraftReflection.getIChatBaseComponentClass(), true);
private static final FieldAccessor PLAYERS = Accessors.getFieldAccessor(SERVER_PING, MinecraftReflection.getServerPingPlayerSampleClass(), true);
private static final FieldAccessor VERSION = Accessors.getFieldAccessor(SERVER_PING, MinecraftReflection.getServerPingServerDataClass(), true);
private static final FieldAccessor FAVICON = Accessors.getFieldAccessor(SERVER_PING, String.class, true);
private static final FieldAccessor[] BOOLEAN_ACCESSORS = Accessors.getFieldAccessorArray(SERVER_PING, boolean.class, true);
// Server ping player sample fields
private static final Class<?> PLAYERS_CLASS = MinecraftReflection.getServerPingPlayerSampleClass();
private static final ConstructorAccessor PLAYERS_CONSTRUCTOR = Accessors.getConstructorAccessor(PLAYERS_CLASS, int.class, int.class);
private static final FieldAccessor[] PLAYERS_INTS = Accessors.getFieldAccessorArray(PLAYERS_CLASS, int.class, true);
private static final FieldAccessor PLAYERS_PROFILES = Accessors.getFieldAccessor(PLAYERS_CLASS, GAME_PROFILE_ARRAY, true);
private static final FieldAccessor PLAYERS_MAXIMUM = PLAYERS_INTS[0];
private static final FieldAccessor PLAYERS_ONLINE = PLAYERS_INTS[1];
// Server ping serialization
private static final Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
private static final MethodAccessor GSON_TO_JSON = Accessors.getMethodAccessor(GSON_CLASS, "toJson", Object.class);
private static final MethodAccessor GSON_FROM_JSON = Accessors.getMethodAccessor(GSON_CLASS, "fromJson", String.class, Class.class);
private static final FieldAccessor PING_GSON = Accessors.getMemorizing(Accessors.getFieldAccessor(
PacketType.Status.Server.SERVER_INFO.getPacketClass(), GSON_CLASS, true
));
// Server data fields
private static final Class<?> VERSION_CLASS = MinecraftReflection.getServerPingServerDataClass();
private static final ConstructorAccessor VERSION_CONSTRUCTOR = Accessors.getConstructorAccessor(VERSION_CLASS, String.class, int.class);
private static final FieldAccessor VERSION_NAME = Accessors.getFieldAccessor(VERSION_CLASS, String.class, true);
private static final FieldAccessor VERSION_PROTOCOL = Accessors.getFieldAccessor(VERSION_CLASS, int.class, true);
// Inner class
private Object players; // may be NULL
private Object version;
/**
* Construct a new server ping initialized with a zero player count, and zero maximum.
* <p>
* Note that the version string is set to 1.9.4.
*/
public LegacyServerPing() {
super(MinecraftReflection.getServerPingClass());
setHandle(SERVER_PING_CONSTRUCTOR.invoke());
resetPlayers();
resetVersion();
}
public LegacyServerPing(Object handle) {
super(MinecraftReflection.getServerPingClass());
setHandle(handle);
this.players = PLAYERS.get(handle);
this.version = VERSION.get(handle);
}
/**
* Set the player count and player maximum to the default values.
*/
public void resetPlayers() {
players = PLAYERS_CONSTRUCTOR.invoke(0, 0);
PLAYERS.set(handle, players);
}
/**
* Reset the version string to the default state.
*/
public void resetVersion() {
MinecraftVersion minecraftVersion = MinecraftVersion.getCurrentVersion();
version = VERSION_CONSTRUCTOR.invoke(minecraftVersion.toString(), MinecraftProtocolVersion.getCurrentVersion());
VERSION.set(handle, version);
}
/**
* Construct a wrapped server ping from a native NMS object.
* @param handle - the native object.
* @return The wrapped server ping object.
*/
public static LegacyServerPing fromHandle(Object handle) {
return new LegacyServerPing(handle);
}
/**
* Construct a wrapper server ping from an encoded JSON string.
* @param json - the JSON string.
* @return The wrapped server ping.
*/
public static LegacyServerPing fromJson(String json) {
return fromHandle(GSON_FROM_JSON.invoke(PING_GSON.get(null), json, SERVER_PING));
}
/**
* Retrieve the message of the day.
* @return The message of the day.
*/
@Override
public WrappedChatComponent getMotD() {
return WrappedChatComponent.fromHandle(DESCRIPTION.get(handle));
}
/**
* Set the message of the day.
* @param description - message of the day.
*/
@Override
public void setMotD(WrappedChatComponent description) {
DESCRIPTION.set(handle, description != null ? description.getHandle() : null);
}
/**
* Retrieve the compressed PNG file that is being displayed as a favicon.
* @return The favicon, or NULL if no favicon will be displayed.
*/
@Override
public String getFavicon() {
return (String) FAVICON.get(handle);
}
/**
* Set the compressed PNG file that is being displayed.
* @param image - the new compressed image or NULL if no favicon should be displayed.
*/
@Override
public void setFavicon(String image) {
FAVICON.set(handle, image);
}
/**
* Retrieve whether chat preview is enabled on the server.
* @return whether chat preview is enabled on the server.
* @since 1.19
* @deprecated Removed in 1.19.3
*/
@Deprecated
public boolean isChatPreviewEnabled() {
return (Boolean) BOOLEAN_ACCESSORS[0].get(handle);
}
/**
* Sets whether chat preview is enabled on the server.
* @param chatPreviewEnabled true if enabled, false otherwise.
* @since 1.19
* @deprecated Removed in 1.19.3
*/
@Deprecated
public void setChatPreviewEnabled(boolean chatPreviewEnabled) {
BOOLEAN_ACCESSORS[0].set(handle, chatPreviewEnabled);
}
/**
* Sets whether the server enforces secure chat.
* @return whether the server enforces secure chat.
* @since 1.19.1
*/
@Override
public boolean isEnforceSecureChat() {
int index = MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove() ? 0 : 1;
return (Boolean) BOOLEAN_ACCESSORS[index].get(handle);
}
/**
* Sets whether the server enforces secure chat.
* @param enforceSecureChat true if enabled, false otherwise.
* @since 1.19.1
*/
@Override
public void setEnforceSecureChat(boolean enforceSecureChat) {
int index = MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove() ? 0 : 1;
BOOLEAN_ACCESSORS[index].set(handle, enforceSecureChat);
}
/**
* Retrieve the displayed number of online players.
* @return The displayed number.
* @throws IllegalStateException If the player count has been hidden via {@link #setPlayersVisible(boolean)}.
* @see #setPlayersOnline(int)
*/
@Override
public int getPlayersOnline() {
if (players == null)
throw new IllegalStateException("The player count has been hidden.");
return (Integer) PLAYERS_ONLINE.get(players);
}
/**
* Set the displayed number of online players.
* <p>
* As of 1.7.2, this is completely unrestricted, and can be both positive and
* negative, as well as higher than the player maximum.
* @param online - online players.
*/
@Override
public void setPlayersOnline(int online) {
if (players == null)
resetPlayers();
PLAYERS_ONLINE.set(players, online);
}
/**
* Retrieve the displayed maximum number of players.
* @return The maximum number.
* @throws IllegalStateException If the player maximum has been hidden via {@link #setPlayersVisible(boolean)}.
* @see #setPlayersMaximum(int)
*/
@Override
public int getPlayersMaximum() {
if (players == null)
throw new IllegalStateException("The player maximum has been hidden.");
return (Integer) PLAYERS_MAXIMUM.get(players);
}
/**
* Set the displayed maximum number of players.
* <p>
* The 1.7.2 accepts any value as a player maximum, positive or negative. It even permits a player maximum that
* is less than the player count.
* @param maximum - maximum player count.
*/
@Override
public void setPlayersMaximum(int maximum) {
if (players == null)
resetPlayers();
PLAYERS_MAXIMUM.set(players, maximum);
}
/**
* Set whether or not the player count and player maximum is visible.
* <p>
* Note that this may set the current player count and maximum to their respective real values.
* @param visible - TRUE if it should be visible, FALSE otherwise.
*/
@Override
public void setPlayersVisible(boolean visible) {
if (arePlayersVisible() != visible) {
if (visible) {
// Recreate the count and maximum
Server server = Bukkit.getServer();
setPlayersMaximum(server.getMaxPlayers());
setPlayersOnline(Bukkit.getOnlinePlayers().size());
} else {
PLAYERS.set(handle, players = null);
}
}
}
@Override
public String getJson() {
return (String) GSON_TO_JSON.invoke(PING_GSON.get(null), getHandle());
}
/**
* Determine if the player count and maximum is visible.
* <p>
* If not, the client will display ??? in the same location.
* @return TRUE if the player statistics is visible, FALSE otherwise.
*/
@Override
public boolean arePlayersVisible() {
return players != null;
}
/**
* Retrieve a copy of all the logged in players.
* @return Logged in players or an empty list if no player names will be displayed.
*/
@Override
public ImmutableList<WrappedGameProfile> getPlayers() {
if (players == null)
return ImmutableList.of();
Object playerProfiles = PLAYERS_PROFILES.get(players);
if (playerProfiles == null)
return ImmutableList.of();
return ImmutableList.copyOf(PROFILE_CONVERT.getSpecific(playerProfiles));
}
/**
* Set the displayed list of logged in players.
* @param playerSample - every logged in player.
*/
@Override
public void setPlayers(Iterable<? extends WrappedGameProfile> playerSample) {
if (players == null)
resetPlayers();
PLAYERS_PROFILES.set(players, PROFILE_CONVERT.getGeneric(playerSample));
}
/**
* Retrieve the version name of the current server.
* @return The version name.
*/
@Override
public String getVersionName() {
return (String) VERSION_NAME.get(version);
}
/**
* Set the version name of the current server.
* @param name - the new version name.
*/
@Override
public void setVersionName(String name) {
VERSION_NAME.set(version, name);
}
/**
* Retrieve the protocol number.
* @return The protocol.
*/
@Override
public int getVersionProtocol() {
return (Integer) VERSION_PROTOCOL.get(version);
}
/**
* Set the version protocol
* @param protocol - the protocol number.
*/
@Override
public void setVersionProtocol(int protocol) {
VERSION_PROTOCOL.set(version, protocol);
}
/**
* Retrieve a deep copy of the current wrapper object.
* @return The current object.
*/
public LegacyServerPing deepClone() {
LegacyServerPing copy = new LegacyServerPing();
WrappedChatComponent motd = getMotD();
copy.setPlayers(getPlayers());
copy.setFavicon(getFavicon());
copy.setMotD(motd != null ? motd : null);
copy.setVersionName(getVersionName());
copy.setVersionProtocol(getVersionProtocol());
if (arePlayersVisible()) {
copy.setPlayersMaximum(getPlayersMaximum());
copy.setPlayersOnline(getPlayersOnline());
} else {
copy.setPlayersVisible(false);
}
return copy;
}
/**
* Retrieve the underlying JSON representation of this server ping.
* @return The JSON representation.
*/
public String toJson() {
return (String) GSON_TO_JSON.invoke(PING_GSON.get(null), handle);
}
@Override
public String toString() {
return "WrappedServerPing< " + toJson() + ">";
}
}
@@ -1,41 +0,0 @@
package com.comphenix.protocol.wrappers.ping;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.google.common.collect.ImmutableList;
public interface ServerPingImpl extends Cloneable {
WrappedChatComponent getMotD();
void setMotD(WrappedChatComponent description);
int getPlayersMaximum();
void setPlayersMaximum(int maxPlayers);
int getPlayersOnline();
void setPlayersOnline(int onlineCount);
ImmutableList<WrappedGameProfile> getPlayers();
void setPlayers(Iterable<? extends WrappedGameProfile> playerSample);
String getVersionName();
void setVersionName(String versionName);
int getVersionProtocol();
void setVersionProtocol(int protocolVersion);
String getFavicon();
void setFavicon(String favicon);
boolean isEnforceSecureChat();
void setEnforceSecureChat(boolean safeChat);
void resetPlayers();
void resetVersion();
default boolean isChatPreviewEnabled() {
return false;
}
default void setChatPreviewEnabled(boolean enabled) {
}
boolean arePlayersVisible();
void setPlayersVisible(boolean visible);
String getJson();
Object getHandle();
}
@@ -1,346 +0,0 @@
package com.comphenix.protocol.wrappers.ping;
import com.comphenix.protocol.events.InternalStructure;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.codecs.WrappedCodec;
import com.comphenix.protocol.wrappers.codecs.WrappedDynamicOps;
import com.google.common.collect.ImmutableList;
import org.bukkit.Bukkit;
import java.nio.charset.StandardCharsets;
import java.util.*;
public final class ServerPingRecord implements ServerPingImpl {
private static Class<?> SERVER_PING;
private static Class<?> PLAYER_SAMPLE_CLASS;
private static Class<?> SERVER_DATA_CLASS;
private static Class<?> GSON_CLASS;
private static MethodAccessor GSON_TO_JSON;
private static MethodAccessor GSON_FROM_JSON;
private static FieldAccessor DATA_SERIALIZER_GSON;
private static Class<?> JSON_ELEMENT_CLASS;
private static WrappedChatComponent DEFAULT_DESCRIPTION;
private static ConstructorAccessor PING_CTOR;
private static WrappedCodec CODEC;
private static EquivalentConverter<List<WrappedGameProfile>> PROFILE_LIST_CONVERTER;
private static boolean initialized = false;
private static final Object lock = new Object();
private static void initialize() {
if (initialized) {
return;
}
synchronized (lock) {
// may have been initialized while waiting for the lock
if (initialized) {
return;
}
try {
SERVER_PING = MinecraftReflection.getServerPingClass();
PLAYER_SAMPLE_CLASS = MinecraftReflection.getServerPingPlayerSampleClass();
SERVER_DATA_CLASS = MinecraftReflection.getServerPingServerDataClass();
PING_CTOR = Accessors.getConstructorAccessor(SERVER_PING.getConstructors()[0]);
DATA_WRAPPER = AutoWrapper.wrap(ServerData.class, SERVER_DATA_CLASS);
SAMPLE_WRAPPER = AutoWrapper.wrap(PlayerSample.class, PLAYER_SAMPLE_CLASS);
FAVICON_WRAPPER = AutoWrapper.wrap(Favicon.class, MinecraftReflection.getMinecraftClass("network.protocol.status.ServerPing$a"));
PROFILE_LIST_CONVERTER = BukkitConverters.getListConverter(BukkitConverters.getWrappedGameProfileConverter());
DEFAULT_DESCRIPTION = WrappedChatComponent.fromLegacyText("A Minecraft Server");
GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
GSON_TO_JSON = Accessors.getMethodAccessor(GSON_CLASS, "toJson", Object.class);
GSON_FROM_JSON = Accessors.getMethodAccessor(GSON_CLASS, "fromJson", String.class, Class.class);
DATA_SERIALIZER_GSON = Accessors.getFieldAccessor(MinecraftReflection.getPacketDataSerializerClass(), GSON_CLASS, true);
JSON_ELEMENT_CLASS = MinecraftReflection.getLibraryClass("com.google.gson.JsonElement");
CODEC = WrappedCodec.fromHandle(Accessors.getFieldAccessor(SERVER_PING, MinecraftReflection.getCodecClass(), false).get(null));
} catch (Exception ex) {
throw new RuntimeException("Failed to initialize Server Ping", ex);
} finally {
initialized = true;
}
}
}
public static final class PlayerSample {
public int max;
public int online;
public Object sample;
public PlayerSample(int max, int online, Object sample) {
this.max = max;
this.online = online;
this.sample = sample;
}
public PlayerSample() {
this(0, 0, null);
}
}
public static final class ServerData {
public String name;
public int protocol;
public ServerData(String name, int protocol) {
this.name = name;
this.protocol = protocol;
}
public ServerData() {
this("", 0);
}
}
static final byte[] EMPTY_FAVICON = new byte[0];
public static final class Favicon {
public byte[] iconBytes;
public Favicon(byte[] iconBytes) {
this.iconBytes = iconBytes;
}
public Favicon() {
this(EMPTY_FAVICON);
}
}
private static AutoWrapper<PlayerSample> SAMPLE_WRAPPER;
private static AutoWrapper<ServerData> DATA_WRAPPER;
private static AutoWrapper<Favicon> FAVICON_WRAPPER;
private WrappedChatComponent description;
private PlayerSample playerSample;
private ServerData serverData;
private Favicon favicon;
private boolean enforceSafeChat;
private boolean playersVisible = true;
private static ServerData defaultData() {
String name = MinecraftVersion.getCurrentVersion().toString();
int protocol = MinecraftProtocolVersion.getCurrentVersion();
return new ServerData(name, protocol);
}
private static PlayerSample defaultSample() {
int max = Bukkit.getMaxPlayers();
int online = Bukkit.getOnlinePlayers().size();
return new PlayerSample(max, online, new ArrayList<>());
}
private static Favicon defaultFavicon() {
return new Favicon();
}
public static ServerPingRecord fromJson(String json) {
Object jsonElement = GSON_FROM_JSON.invoke(DATA_SERIALIZER_GSON.get(null), json, JSON_ELEMENT_CLASS);
Object decoded = CODEC.parse(jsonElement, WrappedDynamicOps.json(false)).getOrThrow(e -> new IllegalStateException("Failed to decode: " + e));
return new ServerPingRecord(decoded);
}
public ServerPingRecord(Object handle) {
initialize();
if(handle.getClass() != SERVER_PING) {
throw new IllegalArgumentException("Expected handle of type " + SERVER_PING.getName() + " but got " + handle.getClass().getName());
}
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
InternalStructure structure = new InternalStructure(handle, modifier);
this.description = structure.getChatComponents().readSafely(0);
StructureModifier<Optional<Object>> optionals = structure.getOptionals(Converters.passthrough(Object.class));
Optional<Object> sampleHandle = optionals.readSafely(0);
this.playerSample = sampleHandle.isPresent() ? SAMPLE_WRAPPER.wrap(sampleHandle.get()) : defaultSample();
Optional<Object> dataHandle = optionals.readSafely(1);
this.serverData = dataHandle.isPresent() ? DATA_WRAPPER.wrap(dataHandle.get()) : defaultData();
Optional<Object> faviconHandle = optionals.readSafely(2);
this.favicon = faviconHandle.isPresent() ? FAVICON_WRAPPER.wrap(faviconHandle.get()) : defaultFavicon();
this.enforceSafeChat = structure.getBooleans().readSafely(0);
}
public ServerPingRecord() {
initialize();
this.description = DEFAULT_DESCRIPTION;
this.playerSample = defaultSample();
this.serverData = defaultData();
this.favicon = defaultFavicon();
}
@Override
public WrappedChatComponent getMotD() {
return description;
}
@Override
public void setMotD(WrappedChatComponent description) {
this.description = description;
}
@Override
public int getPlayersMaximum() {
return playerSample.max;
}
@Override
public void setPlayersMaximum(int maxPlayers) {
playerSample.max = maxPlayers;
}
@Override
public int getPlayersOnline() {
return playerSample.online;
}
@Override
public void setPlayersOnline(int onlineCount) {
playerSample.online = onlineCount;
}
@Override
public ImmutableList<WrappedGameProfile> getPlayers() {
if (playerSample.sample == null) {
return ImmutableList.of();
}
List<WrappedGameProfile> list = PROFILE_LIST_CONVERTER.getSpecific(playerSample.sample);
if (list == null) {
return ImmutableList.of();
}
return ImmutableList.copyOf(list);
}
@Override
public void setPlayers(Iterable<? extends WrappedGameProfile> playerSample) {
if (playerSample == null) {
this.playerSample.sample = null;
return;
}
List<WrappedGameProfile> list = Converters.toList(playerSample);
this.playerSample.sample = PROFILE_LIST_CONVERTER.getGeneric(list);
}
@Override
public String getVersionName() {
return serverData.name;
}
@Override
public void setVersionName(String versionName) {
serverData.name = versionName;
}
@Override
public int getVersionProtocol() {
return serverData.protocol;
}
@Override
public void setVersionProtocol(int protocolVersion) {
serverData.protocol = protocolVersion;
}
@Override
public String getFavicon() {
return new String(favicon.iconBytes, StandardCharsets.UTF_8);
}
@Override
public void setFavicon(String favicon) {
this.favicon.iconBytes = favicon.getBytes(StandardCharsets.UTF_8);
}
@Override
public boolean isEnforceSecureChat() {
return enforceSafeChat;
}
@Override
public void setEnforceSecureChat(boolean safeChat) {
this.enforceSafeChat = safeChat;
}
@Override
public void resetPlayers() {
this.playerSample = defaultSample();
}
@Override
public void resetVersion() {
this.serverData = defaultData();
}
@Override
public boolean arePlayersVisible() {
return playersVisible;
}
@Override
public void setPlayersVisible(boolean visible) {
this.playersVisible = visible;
}
@Override
public String getJson() {
Object encoded = CODEC.encode(getHandle(), WrappedDynamicOps.json(false)).getOrThrow(e -> new IllegalStateException("Failed to encode: " + e));
return (String) GSON_TO_JSON.invoke(DATA_SERIALIZER_GSON.get(null), encoded);
}
@Override
public Object getHandle() {
WrappedChatComponent wrappedDescription = description != null ? description : DEFAULT_DESCRIPTION;
Object descHandle = wrappedDescription.getHandle();
Optional<Object> playersHandle = Optional.ofNullable(SAMPLE_WRAPPER.unwrap(playerSample != null ? playerSample : new ArrayList<>())); // sample has to be non-null in handle
Optional<Object> versionHandle = Optional.ofNullable(serverData != null ? DATA_WRAPPER.unwrap(serverData) : null);
Optional<Object> favHandle = Optional.ofNullable(favicon != null ? FAVICON_WRAPPER.unwrap(favicon) : null);
return PING_CTOR.invoke(descHandle, playersHandle, versionHandle, favHandle, enforceSafeChat);
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof ServerPingRecord)) {
return false;
}
ServerPingRecord other = (ServerPingRecord) obj;
return Objects.equals(description, other.description)
&& Objects.equals(playerSample, other.playerSample)
&& Objects.equals(serverData, other.serverData)
&& ((favicon == null && other.favicon.iconBytes == null)
|| ((favicon != null) == (other.favicon != null) && Arrays.equals(favicon.iconBytes, other.favicon.iconBytes)));
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
name: ProtocolLib
version: ${version}
version: ${project.fullVersion}
description: Provides read/write access to the Minecraft protocol.
authors: [dmulloy2, comphenix]
@@ -14,10 +14,10 @@ import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_19_R3.util.Versioning;
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_19_R2.util.Versioning;
import org.spigotmc.SpigotWorldConfig;
import static org.mockito.Mockito.mock;
@@ -41,24 +41,17 @@ public class BukkitInitialization {
/**
* Statically initializes the mock server for unit testing
*/
public static void initializeAll() {
public static synchronized void initializeAll() {
instance.initialize();
}
private static final Object initLock = new Object();
/**
* Initialize Bukkit and ProtocolLib such that we can perform unit testing
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing
*/
private void initialize() {
if (initialized) {
return;
}
synchronized (initLock) {
if (initialized) {
return;
}
if (!this.initialized) {
// Denote that we're done
this.initialized = true;
try {
LogManager.getLogger();
@@ -111,8 +104,6 @@ public class BukkitInitialization {
// Inject this fake server
Bukkit.setServer(mockedServer);
initialized = true;
}
}
@@ -22,7 +22,6 @@ import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -32,9 +31,6 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import net.minecraft.network.EnumProtocol;
import net.minecraft.network.protocol.EnumProtocolDirection;
import net.minecraft.network.protocol.login.PacketLoginInStart;
@@ -53,9 +49,9 @@ public class PacketTypeTest {
BukkitInitialization.initializeAll();
// I'm well aware this is jank, but it does in fact work correctly and give the desired result
/*PacketType.onDynamicCreate = className -> {
PacketType.onDynamicCreate = className -> {
throw new RuntimeException("Dynamically generated packet " + className);
};*/
};
}
@AfterAll
@@ -65,9 +61,7 @@ public class PacketTypeTest {
}
@SuppressWarnings("unchecked")
// @Test
// public static void main(String[] args) throws Exception {
public void generateNewPackets() throws Exception {
public static void main(String[] args) throws Exception {
MinecraftReflectionTestUtil.init();
Set<Class<?>> allTypes = new HashSet<>();
@@ -77,7 +71,7 @@ public class PacketTypeTest {
for (EnumProtocol protocol : protocols) {
System.out.println(WordUtils.capitalize(protocol.name().toLowerCase()));
Field field = EnumProtocol.class.getDeclaredField("k");
Field field = EnumProtocol.class.getDeclaredField("j");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
@@ -179,35 +173,18 @@ public class PacketTypeTest {
className = classNames.get(classNames.size() - 1);
PacketType existing = null;
try {
existing = PacketType.fromClass(clazz);
if (existing.isDynamic()) {
existing = null;
// Format it like SET_PROTOCOL
StringBuilder fieldName = new StringBuilder();
char[] chars = className.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (i != 0 && Character.isUpperCase(c)) {
fieldName.append("_");
}
} catch (Exception ignored) {
// doesn't exist
fieldName.append(Character.toUpperCase(c));
}
String fieldName;
if (existing == null) {
// Format it like SET_PROTOCOL
StringBuilder fieldNameBuilder = new StringBuilder();
char[] chars = className.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (i != 0 && Character.isUpperCase(c)) {
fieldNameBuilder.append("_");
}
fieldNameBuilder.append(Character.toUpperCase(c));
}
fieldName = fieldNameBuilder.toString().replace("N_B_T", "NBT");
} else {
fieldName = existing.name();
}
builder.append(fieldName);
builder.append(fieldName.toString().replace("N_B_T", "NBT"));
builder.append(" = ");
// Add spacing
@@ -271,6 +248,11 @@ public class PacketTypeTest {
return builder.toString();
}
@BeforeAll
public static void initializeReflection() {
BukkitInitialization.initializeAll();
}
@Test
public void testFindCurrent() {
assertEquals(PacketType.Play.Client.STEER_VEHICLE,
@@ -296,54 +278,42 @@ public class PacketTypeTest {
@Test
@SuppressWarnings("unchecked")
public void ensureTypesAreCorrect() throws Exception {
PacketType.onDynamicCreate = className -> {
throw new RuntimeException("Dynamically generated packet " + className);
};
boolean fail = false;
try {
boolean fail = false;
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
Field field = EnumProtocol.class.getDeclaredField("j");
field.setAccessible(true);
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
Field field = EnumProtocol.class.getDeclaredField("k");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
Map<Integer, Class<?>> treeMap = new TreeMap<>();
for (Entry<Class<?>, Integer> entry1 : reverseMap.entrySet()) {
treeMap.put(entry1.getValue(), entry1.getKey());
}
Map<Integer, Class<?>> treeMap = new TreeMap<>();
for (Entry<Class<?>, Integer> entry1 : reverseMap.entrySet()) {
treeMap.put(entry1.getValue(), entry1.getKey());
}
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
try {
PacketType type = PacketType.fromClass(entry1.getValue());
if (type.getCurrentId() != entry1.getKey()) {
throw new IllegalStateException(
"Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got "
+ type.getCurrentId());
}
} catch (Throwable ex) {
if (ex.getMessage().contains("BundleDelimiterPacket")) {
continue;
}
ex.printStackTrace();
fail = true;
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
try {
PacketType type = PacketType.fromClass(entry1.getValue());
if (type.getCurrentId() != entry1.getKey()) {
throw new IllegalStateException(
"Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got "
+ type.getCurrentId());
}
} catch (Throwable ex) {
ex.printStackTrace();
fail = true;
}
}
}
assertFalse(fail, "Packet type(s) were incorrect!");
} finally {
PacketType.onDynamicCreate = __ -> { };
}
assertFalse(fail, "Packet type(s) were incorrect!");
}
@Test
@@ -361,17 +331,4 @@ public class PacketTypeTest {
}
assertFalse(fail, "Packet type(s) failed to instantiate");
}
@Test
public void testPacketBundleWriting() {
PacketContainer bundlePacket = new PacketContainer(PacketType.Play.Server.BUNDLE);
assertEquals(MinecraftReflection.getPackedBundlePacketClass().orElseThrow(() -> new IllegalStateException("Packet Bundle class is not present")), bundlePacket.getHandle().getClass());
List<PacketContainer> bundle = new ArrayList<>();
PacketContainer chatMessage = new PacketContainer(PacketType.Play.Server.SYSTEM_CHAT);
chatMessage.getStrings().write(0, WrappedChatComponent.fromText("Test").getJson());
chatMessage.getBooleans().write(0, false);
bundle.add(chatMessage);
bundlePacket.getPacketBundles().write(0, bundle);
}
}
@@ -40,13 +40,26 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ComponentConverter;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
import com.comphenix.protocol.wrappers.Pair;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedBlockData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataValue;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedRegistry;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
@@ -555,10 +568,9 @@ public class PacketContainerTest {
}
@Test
@Disabled("effect is now wrapped in a holder") // todo
public void testSoundEffects() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.NAMED_SOUND_EFFECT);
container.getSoundEffects().optionRead(0);
container.getSoundEffects().write(0, Sound.ENTITY_CAT_HISS);
assertEquals(container.getSoundEffects().read(0), Sound.ENTITY_CAT_HISS);
@@ -755,7 +767,7 @@ public class PacketContainerTest {
NativeGameMode.CREATIVE,
new WrappedGameProfile(new UUID(0, 0), "system"),
null,
(WrappedRemoteChatSessionData) null);
null);
updatePlayerInfoActions.getPlayerInfoDataLists().write(1, Collections.singletonList(data));
Set<EnumWrappers.PlayerInfoAction> readActions = updatePlayerInfoActions.getPlayerInfoActions().read(0);
@@ -774,17 +786,21 @@ public class PacketContainerTest {
Assertions.assertEquals(NativeGameMode.CREATIVE, firstData.getGameMode());
}
// TODO: fix this this at some point
/*
@Test
public void testSignedChatMessage() {
PacketContainer chatPacket = new PacketContainer(PacketType.Play.Client.CHAT);
byte[] signature = new byte[256];
WrappedMessageSignature wrappedSignature = new WrappedMessageSignature(signature);
chatPacket.getMessageSignatures().write(0, wrappedSignature);
byte[] signature = new byte[512];
long salt = 124L;
WrappedSaltedSignature wrappedSignature = new WrappedSaltedSignature(salt, signature);
chatPacket.getSignatures().write(0, wrappedSignature);
WrappedMessageSignature read = chatPacket.getMessageSignatures().read(0);
assertArrayEquals(signature, read.getBytes());
}
WrappedSaltedSignature read = chatPacket.getSignatures().read(0);
assertEquals(salt, read.getSalt());
assertArrayEquals(signature, read.getSignature());
}*/
private void assertPacketsEqualAndSerializable(PacketContainer constructed, PacketContainer cloned) {
StructureModifier<Object> firstMod = constructed.getModifier(), secondMod = cloned.getModifier();
@@ -812,8 +828,7 @@ public class PacketContainerTest {
// Try constructing all the packets
for (PacketType type : PacketType.values()) {
// TODO: try to support chat - for now chat contains to many sub classes to properly clone it
if (type.isDeprecated() || !type.isSupported() || type.name().contains("CUSTOM_PAYLOAD") || type.name().contains("CHAT")
|| type.name().contains("BUNDLE")) {
if (type.isDeprecated() || !type.isSupported() || type.name().contains("CUSTOM_PAYLOAD") || type.name().contains("CHAT")) {
continue;
}
@@ -854,6 +869,7 @@ public class PacketContainerTest {
}
// gives some indication which cloning process fails as the checks itself are happening outside this method
System.out.println("Cloning " + type);
// Clone the packet all three ways
PacketContainer shallowCloned = constructed.shallowClone();
@@ -868,8 +884,8 @@ public class PacketContainerTest {
serializedCloned.getLongs().write(0, 0L);
}
this.assertPacketsEqualAndSerializable(constructed, serializedCloned);
} catch (Throwable t) {
Assertions.fail("Unable to clone " + type, t);
} catch (Exception ex) {
Assertions.fail("Unable to clone " + type, ex);
}
}
}
@@ -12,8 +12,8 @@ import net.minecraft.server.level.PlayerChunkMap;
import net.minecraft.server.level.PlayerChunkMap.EntityTracker;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -1,37 +0,0 @@
package com.comphenix.protocol.utility;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class CachedPackageTest {
private CachedPackage pack;
@BeforeEach
public void prepare() {
ClassSource source = ClassSource.fromClassLoader();
this.pack = new CachedPackage("java.lang", source);
}
@Test
public void testGetPackageClass() {
Optional<Class<?>> result = pack.getPackageClass("Object");
assertTrue(result.isPresent());
assertEquals(result.get(), Object.class);
}
@Test
public void testUsingAliases() {
Optional<Class<?>> result = pack.getPackageClass("NOT_A_CLASS", "Object");
assertTrue(result.isPresent());
assertEquals(result.get(), Object.class);
result = pack.getPackageClass("NOT_A_CLASS", "STILL_NOT_A_CLASS", "Object");
assertTrue(result.isPresent());
assertEquals(result.get(), Object.class);
}
}
@@ -13,7 +13,7 @@ import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
@@ -3,7 +3,7 @@ package com.comphenix.protocol.utility;
public class MinecraftReflectionTestUtil {
public static final String RELEASE_TARGET = "1.19.3";
public static final String PACKAGE_VERSION = "v1_19_R3";
public static final String PACKAGE_VERSION = "v1_19_R2";
public static final String NMS = "net.minecraft";
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
@@ -7,15 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.reflect.accessors.Accessors;
import java.lang.reflect.Field;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.time.Instant;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
@@ -57,17 +49,4 @@ public class TestUtils {
public static void setFinalField(Object obj, Field field, Object newValue) {
Accessors.getFieldAccessor(field).set(obj, newValue);
}
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
return keyPairGenerator.generateKeyPair();
}
public static WrappedRemoteChatSessionData creteDummyRemoteChatSessionData() throws Exception {
byte[] signature = new byte[256];
new Random().nextBytes(signature);
return new WrappedRemoteChatSessionData(UUID.randomUUID(), new WrappedProfilePublicKey.WrappedProfileKeyData(Instant.now(), TestUtils.generateKeyPair().getPublic(), signature));
}
}
@@ -8,8 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.minecraft.advancements.AdvancementDisplay;
import net.minecraft.advancements.AdvancementFrameType;
import net.minecraft.network.chat.IChatBaseComponent;
@@ -22,100 +20,100 @@ import org.junit.jupiter.api.Test;
public class AutoWrapperTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
public void testToNms() {
WrappedAdvancementDisplay display = new WrappedAdvancementDisplay();
display.title = WrappedChatComponent.fromText("Test123");
display.description = WrappedChatComponent.fromText("Test567");
display.item = new ItemStack(Material.GOLD_INGOT);
display.background = new MinecraftKey("test");
display.frameType = WrappedFrameType.CHALLENGE;
display.announceChat = false;
display.showToast = true;
display.hidden = true;
display.x = 5f;
display.y = 67f;
@Test
public void testToNms() {
WrappedAdvancementDisplay display = new WrappedAdvancementDisplay();
display.title = WrappedChatComponent.fromText("Test123");
display.description = WrappedChatComponent.fromText("Test567");
display.item = new ItemStack(Material.GOLD_INGOT);
display.background = new MinecraftKey("test");
display.frameType = WrappedFrameType.CHALLENGE;
display.announceChat = false;
display.showToast = true;
display.hidden = true;
display.x = 5f;
display.y = 67f;
AdvancementDisplay nms = (AdvancementDisplay) displayWrapper().unwrap(display);
AdvancementDisplay nms = (AdvancementDisplay) displayWrapper().unwrap(display);
assertTrue(nms.h());
assertTrue(nms.j());
assertFalse(nms.i());
assertEquals("test", nms.d().a());
validateRawText(nms.a(), "Test123");
validateRawText(nms.b(), "Test567");
assertSame(AdvancementFrameType.b, nms.e());
assertSame(MinecraftReflection.getBukkitItemStack(nms.c()).getType(), Material.GOLD_INGOT);
assertEquals(5f, nms.f(), 0f);
assertEquals(67f, nms.g(), 0f);
}
assertTrue(nms.h());
assertTrue(nms.j());
assertFalse(nms.i());
assertEquals("test", nms.d().a());
validateRawText(nms.a(), "Test123");
validateRawText(nms.b(), "Test567");
assertSame(AdvancementFrameType.b, nms.e());
assertSame(Items.nv, nms.c().c());
assertEquals(5f, nms.f(), 0f);
assertEquals(67f, nms.g(), 0f);
}
@Test
public void testFromNms() {
AdvancementDisplay display = new AdvancementDisplay(
(net.minecraft.world.item.ItemStack)MinecraftReflection.getMinecraftItemStack(new ItemStack(Material.ENDER_EYE)),
IChatBaseComponent.b("Test123"),
IChatBaseComponent.b("Test567"),
new net.minecraft.resources.MinecraftKey("minecraft", "test"),
AdvancementFrameType.b,
true,
false,
true
);
display.a(5f, 67f);
@Test
public void testFromNms() {
AdvancementDisplay display = new AdvancementDisplay(
new net.minecraft.world.item.ItemStack(Items.rc),
IChatBaseComponent.b("Test123"),
IChatBaseComponent.b("Test567"),
new net.minecraft.resources.MinecraftKey("minecraft", "test"),
AdvancementFrameType.b,
true,
false,
true
);
display.a(5f, 67f);
WrappedAdvancementDisplay wrapped = displayWrapper().wrap(display);
WrappedAdvancementDisplay wrapped = displayWrapper().wrap(display);
assertTrue(wrapped.showToast);
assertTrue(wrapped.hidden);
assertFalse(wrapped.announceChat);
assertEquals("test", wrapped.background.getKey());
assertEquals("{\"text\":\"Test123\"}", wrapped.title.getJson());
assertEquals("{\"text\":\"Test567\"}", wrapped.description.getJson());
assertSame(WrappedFrameType.CHALLENGE, wrapped.frameType);
assertSame(Material.ENDER_EYE, wrapped.item.getType());
assertEquals(5f, wrapped.x, 0f);
assertEquals(67f, wrapped.y, 0f);
}
assertTrue(wrapped.showToast);
assertTrue(wrapped.hidden);
assertFalse(wrapped.announceChat);
assertEquals("test", wrapped.background.getKey());
assertEquals("{\"text\":\"Test123\"}", wrapped.title.getJson());
assertEquals("{\"text\":\"Test567\"}", wrapped.description.getJson());
assertSame(WrappedFrameType.CHALLENGE, wrapped.frameType);
assertSame(Material.ENDER_EYE, wrapped.item.getType());
assertEquals(5f, wrapped.x, 0f);
assertEquals(67f, wrapped.y, 0f);
}
private AutoWrapper<WrappedAdvancementDisplay> displayWrapper() {
return AutoWrapper
.wrap(WrappedAdvancementDisplay.class, "advancements.AdvancementDisplay", "advancements.DisplayInfo")
.field(0, BukkitConverters.getWrappedChatComponentConverter())
.field(1, BukkitConverters.getWrappedChatComponentConverter())
.field(2, BukkitConverters.getItemStackConverter())
.field(3, MinecraftKey.getConverter())
.field(4, EnumWrappers.getGenericConverter(getMinecraftClass("advancements.AdvancementFrameType", "advancements.FrameType"),
WrappedFrameType.class));
}
private AutoWrapper<WrappedAdvancementDisplay> displayWrapper() {
return AutoWrapper
.wrap(WrappedAdvancementDisplay.class, "advancements.AdvancementDisplay", "advancements.DisplayInfo")
.field(0, BukkitConverters.getWrappedChatComponentConverter())
.field(1, BukkitConverters.getWrappedChatComponentConverter())
.field(2, BukkitConverters.getItemStackConverter())
.field(3, MinecraftKey.getConverter())
.field(4, EnumWrappers.getGenericConverter(getMinecraftClass("advancements.AdvancementFrameType", "advancements.FrameType"),
WrappedFrameType.class));
}
private void validateRawText(IChatBaseComponent component, String expected) {
LiteralContents content = assertInstanceOf(LiteralContents.class, component.b());
assertEquals(expected, content.a());
}
private void validateRawText(IChatBaseComponent component, String expected) {
LiteralContents content = assertInstanceOf(LiteralContents.class, component.b());
assertEquals(expected, content.a());
}
public enum WrappedFrameType {
TASK,
CHALLENGE,
GOAL
}
public enum WrappedFrameType {
TASK,
CHALLENGE,
GOAL
}
public static final class WrappedAdvancementDisplay {
public static final class WrappedAdvancementDisplay {
public WrappedChatComponent title;
public WrappedChatComponent description;
public ItemStack item;
public MinecraftKey background;
public WrappedFrameType frameType;
public boolean showToast;
public boolean announceChat;
public boolean hidden;
public float x;
public float y;
}
public WrappedChatComponent title;
public WrappedChatComponent description;
public ItemStack item;
public MinecraftKey background;
public WrappedFrameType frameType;
public boolean showToast;
public boolean announceChat;
public boolean hidden;
public float x;
public float y;
}
}
@@ -1,12 +1,12 @@
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.TestUtils;
import com.comphenix.protocol.wrappers.Either.Left;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
@@ -16,41 +16,37 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.*;
public class BukkitConvertersTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
item.setItemMeta(meta);
@Test
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
item.setItemMeta(meta);
EquivalentConverter<ItemStack> converter = BukkitConverters.getItemStackConverter();
Object nmsStack = converter.getGeneric(item);
ItemStack back = converter.getSpecific(nmsStack);
EquivalentConverter<ItemStack> converter = BukkitConverters.getItemStackConverter();
Object nmsStack = converter.getGeneric(item);
ItemStack back = converter.getSpecific(nmsStack);
assertEquals(item.getType(), back.getType());
assertEquals(item.getDurability(), back.getDurability());
assertEquals(item.hasItemMeta(), back.hasItemMeta());
assertTrue(Bukkit.getItemFactory().equals(item.getItemMeta(), back.getItemMeta()));
}
assertEquals(item.getType(), back.getType());
assertEquals(item.getDurability(), back.getDurability());
assertEquals(item.hasItemMeta(), back.hasItemMeta());
assertTrue(Bukkit.getItemFactory().equals(item.getItemMeta(), back.getItemMeta()));
}
@Test
public void testEither() {
Either<String, String> test = new Left<>("bla");
EquivalentConverter<Either<String, String>> converter = BukkitConverters.getEitherConverter(
Converters.passthrough(String.class), Converters.passthrough(String.class)
Converters.passthrough(String.class), Converters.passthrough(String.class)
);
com.mojang.datafixers.util.Either<String, String> nmsEither = (com.mojang.datafixers.util.Either<String, String>) converter.getGeneric(test);
@@ -59,36 +55,4 @@ public class BukkitConvertersTest {
assertEquals(wrapped.left(), nmsEither.left());
assertEquals(wrapped.right(), nmsEither.right());
}
@Test
public void testPacketContainerConverter() {
for (PacketType type : PacketType.values()) {
if(!type.isSupported()) {
continue;
}
PacketContainer container = new PacketContainer(type);
Object generic = BukkitConverters.getPacketContainerConverter().getGeneric(container);
Object specific = BukkitConverters.getPacketContainerConverter().getSpecific(generic);
assertTrue(EqualsBuilder.reflectionEquals(container, specific)); // PacketContainer does not properly implement equals(.)
}
}
@Test
void getWrappedMessageSignatureConverter() {
byte[] data = new byte[256];
new Random().nextBytes(data);
WrappedMessageSignature messageSignature = new WrappedMessageSignature(data);
assertArrayEquals(data, BukkitConverters.getWrappedMessageSignatureConverter().getSpecific(BukkitConverters.getWrappedMessageSignatureConverter().getGeneric(messageSignature)).getBytes());
}
@Test
public void testRemoteChatSessionDataConverter() throws Exception {
WrappedRemoteChatSessionData wrappedRemoteChatSessionData = TestUtils.creteDummyRemoteChatSessionData();
Object generic = BukkitConverters.getWrappedRemoteChatSessionDataConverter().getGeneric(wrappedRemoteChatSessionData);
WrappedRemoteChatSessionData specific = BukkitConverters.getWrappedRemoteChatSessionDataConverter().getSpecific(generic);
assertEquals(wrappedRemoteChatSessionData, specific);
}
}
@@ -1,27 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author Lukas Alt
* @since 26.03.2023
*/
public class ConverterTest {
@Test
public void testIterableConverter() {
EquivalentConverter<Iterable<String>> converter = Converters.iterable(Converters.passthrough(String.class), ArrayList::new, ArrayList::new);
List<String> l = Arrays.asList("a", "b", "c");
Object generic = converter.getGeneric(l);
Object specific = converter.getSpecific(generic);
assertEquals(l, specific);
}
}
@@ -17,7 +17,6 @@ package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.TestUtils;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
@@ -34,19 +33,14 @@ public class PlayerInfoDataTest {
}
@Test
public void test() throws Exception {
public void test() {
WrappedGameProfile profile = new WrappedGameProfile(UUID.randomUUID(), "Name");
WrappedChatComponent displayName = WrappedChatComponent.fromText("Name's Name");
testWriteBack(new PlayerInfoData(profile, 42, NativeGameMode.CREATIVE, displayName));
testWriteBack(new PlayerInfoData(profile.getUUID(), 42, false, NativeGameMode.CREATIVE, profile, displayName, TestUtils.creteDummyRemoteChatSessionData()));
testWriteBack(new PlayerInfoData(profile.getUUID(), 42, false, NativeGameMode.CREATIVE, null, null, TestUtils.creteDummyRemoteChatSessionData()));
testWriteBack(new PlayerInfoData(profile.getUUID(), 42, true, NativeGameMode.CREATIVE, null, displayName));
}
private static void testWriteBack(PlayerInfoData data) {
PlayerInfoData data = new PlayerInfoData(profile, 42, NativeGameMode.CREATIVE, displayName);
Object generic = PlayerInfoData.getConverter().getGeneric(data);
PlayerInfoData back = PlayerInfoData.getConverter().getSpecific(generic);
assertEquals(data, back);
}
}
@@ -19,9 +19,9 @@ import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.GlassPane;
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R3.block.impl.CraftStainedGlassPane;
import org.bukkit.craftbukkit.v1_19_R3.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R2.block.impl.CraftStainedGlassPane;
import org.bukkit.craftbukkit.v1_19_R2.util.CraftMagicNumbers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -56,7 +56,7 @@ public class WrappedBlockDataTest {
@Test
public void testDataCreation() {
IBlockData nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).o();
IBlockData nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).n();
GlassPane data = (GlassPane) CraftBlockData.fromData(nmsData);
data.setFace(BlockFace.EAST, true);
@@ -21,8 +21,8 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import net.minecraft.world.entity.projectile.EntityEgg;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEgg;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEgg;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -25,7 +25,7 @@ import net.minecraft.world.level.chunk.ILightAccess;
import net.minecraft.world.level.lighting.LightEngine;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -58,7 +58,7 @@ public class WrappedLevelChunkDataTest {
when(nmsWorld.v_()).thenReturn(256);
when(nmsWorld.ai()).thenReturn(16); // LevelHeightAccessor is mocked and therefore always returns 0, there are further methods like this which might cause errors in the future
when(nmsWorld.l_()).thenReturn(engine);
when(nmsWorld.m_()).thenReturn(engine);
}
private final WorldServer nmsWorld;
@@ -69,7 +69,7 @@ public class WrappedLevelChunkDataTest {
this.nmsWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
this.chunk = new Chunk(nmsWorld, new ChunkCoordIntPair(5, 5));
IBlockData bellData = BuiltInRegistries.f.a(new MinecraftKey("bell")).o();
IBlockData bellData = BuiltInRegistries.f.a(new MinecraftKey("bell")).n();
chunk.b(0).a(0, 0, 0, bellData);
chunk.a(new TileEntityBell(BlockPosition.b, bellData));
@@ -77,7 +77,7 @@ public class WrappedLevelChunkDataTest {
@Test
public void testChunkData() {
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(chunk, nmsWorld.l_(), null, null, false);
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(chunk, nmsWorld.m_(), null, null, false);
PacketContainer container = PacketContainer.fromPacket(packet);
Object rawInstance = container.getSpecificModifier(MinecraftReflection.getLevelChunkPacketDataClass()).read(0);
Object virtualInstance = BukkitConverters.getWrappedChunkDataConverter().getGeneric(container.getLevelChunkData().read(0));
@@ -117,7 +117,7 @@ public class WrappedLevelChunkDataTest {
@Test
public void testLightData() {
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(chunk, nmsWorld.l_(), null, null, false);
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(chunk, nmsWorld.m_(), null, null, false);
PacketContainer container = PacketContainer.fromPacket(packet);
randomizeBitSets(container.getSpecificModifier(MinecraftReflection.getLightUpdatePacketDataClass()).read(0));
@@ -1,21 +1,17 @@
package com.comphenix.protocol.wrappers;
import java.io.IOException;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.wrappers.WrappedServerPing.CompressedImage;
import com.google.common.io.Resources;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import static org.junit.jupiter.api.Assertions.*;
public class WrappedServerPingTest {
@BeforeAll
@@ -24,80 +20,32 @@ public class WrappedServerPingTest {
}
@Test
public void fullTest() throws IOException {
PacketContainer packet = new PacketContainer(PacketType.Status.Server.SERVER_INFO);
Optional<WrappedServerPing> optionalPing = packet.getServerPings().optionRead(0);
assertTrue(optionalPing.isPresent());
public void test() {
try {
CompressedImage tux = CompressedImage.fromPng(Resources.getResource("tux.png").openStream());
byte[] original = tux.getDataCopy();
WrappedServerPing serverPing = optionalPing.get();
assertNotNull(serverPing.getMotD());
assertNotNull(serverPing.getFavicon());
assertNotNull(serverPing.getPlayers());
assertNotNull(serverPing.getVersionName());
WrappedServerPing serverPing = new WrappedServerPing();
serverPing.setMotD("Hello, this is a test.");
serverPing.setPlayersOnline(5);
serverPing.setPlayersMaximum(10);
serverPing.setVersionName("Minecraft 123");
serverPing.setVersionProtocol(4);
serverPing.setFavicon(tux);
serverPing.setEnforceSecureChat(true);
CompressedImage tux = CompressedImage.fromPng(Resources.getResource("tux.png").openStream());
byte[] original = tux.getDataCopy();
assertEquals(5, serverPing.getPlayersOnline());
assertEquals(10, serverPing.getPlayersMaximum());
assertEquals("Minecraft 123", serverPing.getVersionName());
assertEquals(4, serverPing.getVersionProtocol());
assertTrue(serverPing.isEnforceSecureChat());
serverPing.setMotD("Hello, this is a test.");
serverPing.setPlayersOnline(5);
serverPing.setPlayersMaximum(10);
serverPing.setVersionName("Minecraft 123");
serverPing.setVersionProtocol(4);
serverPing.setFavicon(tux);
serverPing.setEnforceSecureChat(true);
assertArrayEquals(original, serverPing.getFavicon().getData());
packet.getServerPings().write(0, serverPing);
WrappedServerPing roundTrip = packet.getServerPings().read(0);
String asJson = serverPing.toJson();
WrappedServerPing deserialized = WrappedServerPing.fromJson(asJson);
deserialized.setMotD(serverPing.getMotD());
// Deserializing to JSON and parsing the JSON again can lead to a different object as the Mojang Datafixer optimizes server icons or reorders components in the server description
assertEquals(serverPing.getVersionName(), deserialized.getVersionName(), "Failed to serialize as JSON and deserialize afterwards (version name mismatch)");
assertEquals(serverPing.getVersionProtocol(), deserialized.getVersionProtocol(), "Failed to serialize as JSON and deserialize afterwards (version protocol mismatch)");
assertEquals(serverPing.getPlayersOnline(), deserialized.getPlayersOnline(), "Failed to serialize as JSON and deserialize afterwards (players online mismatch)");
assertEquals(serverPing.getPlayersMaximum(), deserialized.getPlayersMaximum(), "Failed to serialize as JSON and deserialize afterwards (players maximum mismatch)");
assertEquals(serverPing.getPlayers(), deserialized.getPlayers(), "Failed to serialize as JSON and deserialize afterwards (player sample mismatch)");
assertEquals(PlainTextComponentSerializer.plainText().serialize(AdventureComponentConverter.fromWrapper(serverPing.getMotD())), PlainTextComponentSerializer.plainText().serialize(AdventureComponentConverter.fromWrapper(deserialized.getMotD()))); // Check if plain text is equivalent
assertEquals(5, roundTrip.getPlayersOnline());
assertEquals(10, roundTrip.getPlayersMaximum());
assertEquals("Minecraft 123", roundTrip.getVersionName());
assertEquals(4, roundTrip.getVersionProtocol());
assertTrue(roundTrip.isEnforceSecureChat());
assertArrayEquals(original, roundTrip.getFavicon().getData());
CompressedImage copy = CompressedImage.fromBase64Png(Base64Coder.encodeLines(tux.getData()));
assertArrayEquals(copy.getData(), roundTrip.getFavicon().getData());
}
@Test
public void testDefaultData() {
PacketContainer packet = new PacketContainer(PacketType.Status.Server.SERVER_INFO);
packet.getServerPings().write(0, new WrappedServerPing());
WrappedServerPing serverPing = packet.getServerPings().read(0);
assertEquals(serverPing.getMotD(), WrappedChatComponent.fromLegacyText("A Minecraft Server"));
assertEquals(serverPing.getVersionProtocol(), MinecraftProtocolVersion.getCurrentVersion());
}
@Test
public void testSetPartialData() {
PacketContainer packet = new PacketContainer(PacketType.Status.Server.SERVER_INFO);
WrappedServerPing serverPing = new WrappedServerPing();
serverPing.setPlayersOnline(69);
serverPing.setPlayersMaximum(420);
packet.getServerPings().write(0, serverPing);
WrappedServerPing roundTrip = packet.getServerPings().read(0);
assertEquals(roundTrip.getMotD(), WrappedChatComponent.fromLegacyText("A Minecraft Server"));
assertEquals(roundTrip.getVersionProtocol(), MinecraftProtocolVersion.getCurrentVersion());
assertEquals(roundTrip.getPlayersOnline(), 69);
assertEquals(roundTrip.getPlayersMaximum(), 420);
CompressedImage copy = CompressedImage.fromBase64Png(Base64Coder.encodeLines(tux.getData()));
assertArrayEquals(copy.getData(), serverPing.getFavicon().getData());
} catch (Throwable ex) {
fail("Encountered an exception testing ServerPing", ex);
}
}
}