Add Maven !
This commit is contained in:
parent
f74d57962a
commit
c9a58ccd48
|
@ -0,0 +1,71 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-api</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-commons</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-nms-v1_8_R1</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-nms-v1_8_R2</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-nms-v1_8_R3</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-nms-v1_9_R1</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-nms-v1_9_R2</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-nms-v1_10_R1</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-nms-v1_11_R1</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-nms-v1_12_R1</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-commons</artifactId>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,393 @@
|
||||||
|
package com.comphenix.tinyprotocol;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An utility class that simplifies reflection in Bukkit plugins.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public final class Reflection {
|
||||||
|
/**
|
||||||
|
* An interface for invoking a specific constructor.
|
||||||
|
*/
|
||||||
|
public interface ConstructorInvoker {
|
||||||
|
/**
|
||||||
|
* Invoke a constructor for a specific class.
|
||||||
|
*
|
||||||
|
* @param arguments - the arguments to pass to the constructor.
|
||||||
|
* @return The constructed object.
|
||||||
|
*/
|
||||||
|
Object invoke(Object... arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for invoking a specific method.
|
||||||
|
*/
|
||||||
|
public interface MethodInvoker {
|
||||||
|
/**
|
||||||
|
* Invoke a method on a specific target object.
|
||||||
|
*
|
||||||
|
* @param target - the target object, or NULL for a static method.
|
||||||
|
* @param arguments - the arguments to pass to the method.
|
||||||
|
* @return The return value, or NULL if is void.
|
||||||
|
*/
|
||||||
|
Object invoke(Object target, Object... arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for retrieving the field content.
|
||||||
|
*
|
||||||
|
* @param <T> - field type.
|
||||||
|
*/
|
||||||
|
public interface FieldAccessor<T> {
|
||||||
|
/**
|
||||||
|
* Retrieve the content of a field.
|
||||||
|
*
|
||||||
|
* @param target - the target object, or NULL for a static field.
|
||||||
|
* @return The value of the field.
|
||||||
|
*/
|
||||||
|
T get(Object target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content of a field.
|
||||||
|
*
|
||||||
|
* @param target - the target object, or NULL for a static field.
|
||||||
|
* @param value - the new value of the field.
|
||||||
|
*/
|
||||||
|
void set(Object target, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given object has this field.
|
||||||
|
*
|
||||||
|
* @param target - the object to test.
|
||||||
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
boolean hasField(Object target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduce the net.minecraft.server.v* package
|
||||||
|
private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
|
||||||
|
private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||||
|
private static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "");
|
||||||
|
|
||||||
|
// Variable replacement
|
||||||
|
private static Pattern MATCH_VARIABLE = Pattern.compile("\\{([^}]+)\\}");
|
||||||
|
|
||||||
|
private Reflection() {
|
||||||
|
// Seal class
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor for a specific field type and name.
|
||||||
|
*
|
||||||
|
* @param target - the target type.
|
||||||
|
* @param name - the name of the field, or NULL to ignore.
|
||||||
|
* @param fieldType - a compatible field type.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType) {
|
||||||
|
return getField(target, name, fieldType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor for a specific field type and name.
|
||||||
|
*
|
||||||
|
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||||
|
* @param name - the name of the field, or NULL to ignore.
|
||||||
|
* @param fieldType - a compatible field type.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static <T> FieldAccessor<T> getField(String className, String name, Class<T> fieldType) {
|
||||||
|
return getField(getClass(className), name, fieldType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor for a specific field type and name.
|
||||||
|
*
|
||||||
|
* @param target - the target type.
|
||||||
|
* @param fieldType - a compatible field type.
|
||||||
|
* @param index - the number of compatible fields to skip.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index) {
|
||||||
|
return getField(target, null, fieldType, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field accessor for a specific field type and name.
|
||||||
|
*
|
||||||
|
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||||
|
* @param fieldType - a compatible field type.
|
||||||
|
* @param index - the number of compatible fields to skip.
|
||||||
|
* @return The field accessor.
|
||||||
|
*/
|
||||||
|
public static <T> FieldAccessor<T> getField(String className, Class<T> fieldType, int index) {
|
||||||
|
return getField(getClass(className), fieldType, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common method
|
||||||
|
private static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType, int index) {
|
||||||
|
for (final Field field : target.getDeclaredFields()) {
|
||||||
|
if ((name == null || field.getName().equals(name)) && fieldType.isAssignableFrom(field.getType()) && index-- <= 0) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
// A function for retrieving a specific field value
|
||||||
|
return new FieldAccessor<T>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T get(Object target) {
|
||||||
|
try {
|
||||||
|
return (T) field.get(target);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Cannot access reflection.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Object target, Object value) {
|
||||||
|
try {
|
||||||
|
field.set(target, value);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Cannot access reflection.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasField(Object target) {
|
||||||
|
// target instanceof DeclaringClass
|
||||||
|
return field.getDeclaringClass().isAssignableFrom(target.getClass());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search in parent classes
|
||||||
|
if (target.getSuperclass() != null)
|
||||||
|
return getField(target.getSuperclass(), name, fieldType, index);
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||||
|
* @param methodName - the method name, or NULL to skip.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this specific method.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static MethodInvoker getMethod(String className, String methodName, Class<?>... params) {
|
||||||
|
return getTypedMethod(getClass(className), methodName, null, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param clazz - a class to start with.
|
||||||
|
* @param methodName - the method name, or NULL to skip.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this specific method.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static MethodInvoker getMethod(Class<?> clazz, String methodName, Class<?>... params) {
|
||||||
|
return getTypedMethod(clazz, methodName, null, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param clazz - a class to start with.
|
||||||
|
* @param methodName - the method name, or NULL to skip.
|
||||||
|
* @param returnType - the expected return type, or NULL to ignore.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this specific method.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static MethodInvoker getTypedMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... params) {
|
||||||
|
for (final Method method : clazz.getDeclaredMethods()) {
|
||||||
|
if ((methodName == null || method.getName().equals(methodName))
|
||||||
|
&& (returnType == null || method.getReturnType().equals(returnType))
|
||||||
|
&& Arrays.equals(method.getParameterTypes(), params)) {
|
||||||
|
method.setAccessible(true);
|
||||||
|
|
||||||
|
return (target, arguments) -> {
|
||||||
|
try {
|
||||||
|
return method.invoke(target, arguments);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Cannot invoke method " + method, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search in every superclass
|
||||||
|
if (clazz.getSuperclass() != null)
|
||||||
|
return getMethod(clazz.getSuperclass(), methodName, params);
|
||||||
|
|
||||||
|
throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publically and privately defined constructor of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this constructor.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static ConstructorInvoker getConstructor(String className, Class<?>... params) {
|
||||||
|
return getConstructor(getClass(className), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for the first publicly and privately defined constructor of the given name and parameter count.
|
||||||
|
*
|
||||||
|
* @param clazz - a class to start with.
|
||||||
|
* @param params - the expected parameters.
|
||||||
|
* @return An object that invokes this constructor.
|
||||||
|
* @throws IllegalStateException If we cannot find this method.
|
||||||
|
*/
|
||||||
|
public static ConstructorInvoker getConstructor(Class<?> clazz, Class<?>... params) {
|
||||||
|
for (final Constructor<?> constructor : clazz.getDeclaredConstructors()) {
|
||||||
|
if (Arrays.equals(constructor.getParameterTypes(), params)) {
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
|
||||||
|
return arguments -> {
|
||||||
|
try {
|
||||||
|
return constructor.newInstance(arguments);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Cannot invoke constructor " + constructor, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class from its full name, without knowing its type on compile time.
|
||||||
|
* <p>
|
||||||
|
* This is useful when looking up fields by a NMS or OBC type.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param lookupName - the class name with variables.
|
||||||
|
* @return The class.
|
||||||
|
* @see {@link #getClass()} for more information.
|
||||||
|
*/
|
||||||
|
public static Class<Object> getUntypedClass(String lookupName) {
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
Class<Object> clazz = (Class) getClass(lookupName);
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class from its full name.
|
||||||
|
* <p>
|
||||||
|
* Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
|
||||||
|
*
|
||||||
|
* <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.
|
||||||
|
* @return The looked up class.
|
||||||
|
* @throws IllegalArgumentException If a variable or class could not be found.
|
||||||
|
*/
|
||||||
|
public static Class<?> getClass(String lookupName) {
|
||||||
|
return getCanonicalClass(expandVariables(lookupName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class in the net.minecraft.server.VERSION.* package.
|
||||||
|
*
|
||||||
|
* @param name - the name of the class, excluding the package.
|
||||||
|
* @throws IllegalArgumentException If the class doesn't exist.
|
||||||
|
*/
|
||||||
|
public static Class<?> getMinecraftClass(String name) {
|
||||||
|
return getCanonicalClass(NMS_PREFIX + "." + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class in the org.bukkit.craftbukkit.VERSION.* package.
|
||||||
|
*
|
||||||
|
* @param name - the name of the class, excluding the package.
|
||||||
|
* @throws IllegalArgumentException If the class doesn't exist.
|
||||||
|
*/
|
||||||
|
public static Class<?> getCraftBukkitClass(String name) {
|
||||||
|
return getCanonicalClass(OBC_PREFIX + "." + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class by its canonical name.
|
||||||
|
*
|
||||||
|
* @param canonicalName - the canonical name.
|
||||||
|
* @return The class.
|
||||||
|
*/
|
||||||
|
private static Class<?> getCanonicalClass(String canonicalName) {
|
||||||
|
try {
|
||||||
|
return Class.forName(canonicalName);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot find " + canonicalName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand variables such as "{nms}" and "{obc}" to their corresponding packages.
|
||||||
|
*
|
||||||
|
* @param name - the full name of the class.
|
||||||
|
* @return The expanded string.
|
||||||
|
*/
|
||||||
|
private static String expandVariables(String name) {
|
||||||
|
StringBuffer output = new StringBuffer();
|
||||||
|
Matcher matcher = MATCH_VARIABLE.matcher(name);
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
String variable = matcher.group(1);
|
||||||
|
String replacement;
|
||||||
|
|
||||||
|
// Expand all detected variables
|
||||||
|
if ("nms".equalsIgnoreCase(variable))
|
||||||
|
replacement = NMS_PREFIX;
|
||||||
|
else if ("obc".equalsIgnoreCase(variable))
|
||||||
|
replacement = OBC_PREFIX;
|
||||||
|
else if ("version".equalsIgnoreCase(variable))
|
||||||
|
replacement = VERSION;
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Unknown variable: " + variable);
|
||||||
|
|
||||||
|
// Assume the expanded variables are all packages, and append a dot
|
||||||
|
if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.')
|
||||||
|
replacement += ".";
|
||||||
|
matcher.appendReplacement(output, Matcher.quoteReplacement(replacement));
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher.appendTail(output);
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,501 @@
|
||||||
|
package com.comphenix.tinyprotocol;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection.FieldAccessor;
|
||||||
|
import com.comphenix.tinyprotocol.Reflection.MethodInvoker;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import io.netty.channel.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
|
import org.bukkit.event.server.PluginDisableEvent;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a very tiny alternative to ProtocolLib.
|
||||||
|
* <p>
|
||||||
|
* It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)!
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public abstract class TinyProtocol {
|
||||||
|
private static final AtomicInteger ID = new AtomicInteger(0);
|
||||||
|
|
||||||
|
// Used in order to lookup a channel
|
||||||
|
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
|
||||||
|
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");
|
||||||
|
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.getMinecraftClass("PacketLoginInStart");
|
||||||
|
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
|
||||||
|
|
||||||
|
// Speedup channel lookup
|
||||||
|
private Map<String, Channel> channelLookup = new MapMaker().weakValues().makeMap();
|
||||||
|
private Listener listener;
|
||||||
|
|
||||||
|
// Channels that have already been removed
|
||||||
|
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
|
||||||
|
|
||||||
|
// List of network markers
|
||||||
|
private List<Object> networkManagers;
|
||||||
|
|
||||||
|
// Injected channel handlers
|
||||||
|
private List<Channel> serverChannels = Lists.newArrayList();
|
||||||
|
private ChannelInboundHandlerAdapter serverChannelHandler;
|
||||||
|
private ChannelInitializer<Channel> beginInitProtocol;
|
||||||
|
private ChannelInitializer<Channel> endInitProtocol;
|
||||||
|
|
||||||
|
// Current handler name
|
||||||
|
private String handlerName;
|
||||||
|
|
||||||
|
protected volatile boolean closed;
|
||||||
|
protected Plugin plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients.
|
||||||
|
* <p>
|
||||||
|
* You can construct multiple instances per plugin.
|
||||||
|
*
|
||||||
|
* @param plugin - the plugin.
|
||||||
|
*/
|
||||||
|
public TinyProtocol(final Plugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
// Compute handler name
|
||||||
|
this.handlerName = getHandlerName();
|
||||||
|
|
||||||
|
// Prepare existing players
|
||||||
|
registerBukkitEvents();
|
||||||
|
|
||||||
|
try {
|
||||||
|
registerChannelHandler();
|
||||||
|
registerPlayers(plugin);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
// Damn you, late bind
|
||||||
|
plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind.");
|
||||||
|
|
||||||
|
new BukkitRunnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
registerChannelHandler();
|
||||||
|
registerPlayers(plugin);
|
||||||
|
plugin.getLogger().info("[TinyProtocol] Late bind injection successful.");
|
||||||
|
}
|
||||||
|
}.runTask(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createServerChannelHandler() {
|
||||||
|
// Handle connected channels
|
||||||
|
endInitProtocol = new ChannelInitializer<Channel>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel channel) throws Exception {
|
||||||
|
try {
|
||||||
|
// This can take a while, so we need to stop the main thread from interfering
|
||||||
|
synchronized (networkManagers) {
|
||||||
|
// Stop injecting channels
|
||||||
|
if (!closed) {
|
||||||
|
channel.eventLoop().submit(() -> injectChannelInternal(channel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is executed before Minecraft's channel handler
|
||||||
|
beginInitProtocol = new ChannelInitializer<Channel>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel channel) throws Exception {
|
||||||
|
channel.pipeline().addLast(endInitProtocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
serverChannelHandler = new ChannelInboundHandlerAdapter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
|
Channel channel = (Channel) msg;
|
||||||
|
|
||||||
|
// Prepare to initialize ths channel
|
||||||
|
channel.pipeline().addFirst(beginInitProtocol);
|
||||||
|
ctx.fireChannelRead(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register bukkit events.
|
||||||
|
*/
|
||||||
|
private void registerBukkitEvents() {
|
||||||
|
listener = new Listener() {
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public final void onPlayerLogin(PlayerLoginEvent e) {
|
||||||
|
if (closed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Channel channel = getChannel(e.getPlayer());
|
||||||
|
|
||||||
|
// Don't inject players that have been explicitly uninjected
|
||||||
|
if (!uninjectedChannels.contains(channel)) {
|
||||||
|
injectPlayer(e.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public final void onPluginDisable(PluginDisableEvent e) {
|
||||||
|
if (e.getPlugin().equals(plugin)) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
plugin.getServer().getPluginManager().registerEvents(listener, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void registerChannelHandler() {
|
||||||
|
Object mcServer = getMinecraftServer.get(Bukkit.getServer());
|
||||||
|
Object serverConnection = getServerConnection.get(mcServer);
|
||||||
|
boolean looking = true;
|
||||||
|
|
||||||
|
// We need to synchronize against this list
|
||||||
|
networkManagers = (List<Object>) getNetworkMarkers.invoke(null, serverConnection);
|
||||||
|
createServerChannelHandler();
|
||||||
|
|
||||||
|
// Find the correct list, or implicitly throw an exception
|
||||||
|
for (int i = 0; looking; i++) {
|
||||||
|
List<Object> list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection);
|
||||||
|
|
||||||
|
for (Object item : list) {
|
||||||
|
if (!ChannelFuture.class.isInstance(item))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Channel future that contains the server connection
|
||||||
|
Channel serverChannel = ((ChannelFuture) item).channel();
|
||||||
|
|
||||||
|
serverChannels.add(serverChannel);
|
||||||
|
serverChannel.pipeline().addFirst(serverChannelHandler);
|
||||||
|
looking = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterChannelHandler() {
|
||||||
|
if (serverChannelHandler == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (Channel serverChannel : serverChannels) {
|
||||||
|
final ChannelPipeline pipeline = serverChannel.pipeline();
|
||||||
|
|
||||||
|
// Remove channel handler
|
||||||
|
serverChannel.eventLoop().execute(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
pipeline.remove(serverChannelHandler);
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// That's fine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerPlayers(Plugin plugin) {
|
||||||
|
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||||
|
injectPlayer(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the server is starting to send a packet to a player.
|
||||||
|
* <p>
|
||||||
|
* Note that this is not executed on the main thread.
|
||||||
|
*
|
||||||
|
* @param receiver - the receiving player, NULL for early login/status packets.
|
||||||
|
* @param channel - the channel that received the packet. Never NULL.
|
||||||
|
* @param packet - the packet being sent.
|
||||||
|
* @return The packet to send instead, or NULL to cancel the transmission.
|
||||||
|
*/
|
||||||
|
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the server has received a packet from a given player.
|
||||||
|
* <p>
|
||||||
|
* Use {@link Channel#remoteAddress()} to get the remote address of the client.
|
||||||
|
*
|
||||||
|
* @param sender - the player that sent the packet, NULL for early login/status packets.
|
||||||
|
* @param channel - channel that received the packet. Never NULL.
|
||||||
|
* @param packet - the packet being received.
|
||||||
|
* @return The packet to recieve instead, or NULL to cancel.
|
||||||
|
*/
|
||||||
|
public Object onPacketInAsync(Player sender, Channel channel, Object packet) {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet to a particular player.
|
||||||
|
* <p>
|
||||||
|
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||||
|
*
|
||||||
|
* @param player - the destination player.
|
||||||
|
* @param packet - the packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(Player player, Object packet) {
|
||||||
|
sendPacket(getChannel(player), packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet to a particular client.
|
||||||
|
* <p>
|
||||||
|
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||||
|
*
|
||||||
|
* @param channel - client identified by a channel.
|
||||||
|
* @param packet - the packet to send.
|
||||||
|
*/
|
||||||
|
public void sendPacket(Channel channel, Object packet) {
|
||||||
|
channel.pipeline().writeAndFlush(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pretend that a given packet has been received from a player.
|
||||||
|
* <p>
|
||||||
|
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||||
|
*
|
||||||
|
* @param player - the player that sent the packet.
|
||||||
|
* @param packet - the packet that will be received by the server.
|
||||||
|
*/
|
||||||
|
public void receivePacket(Player player, Object packet) {
|
||||||
|
receivePacket(getChannel(player), packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pretend that a given packet has been received from a given client.
|
||||||
|
* <p>
|
||||||
|
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
|
||||||
|
*
|
||||||
|
* @param channel - client identified by a channel.
|
||||||
|
* @param packet - the packet that will be received by the server.
|
||||||
|
*/
|
||||||
|
public void receivePacket(Channel channel, Object packet) {
|
||||||
|
channel.pipeline().context("encoder").fireChannelRead(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the name of the channel injector, default implementation is "tiny-" + plugin name + "-" + a unique ID.
|
||||||
|
* <p>
|
||||||
|
* Note that this method will only be invoked once. It is no longer necessary to override this to support multiple instances.
|
||||||
|
*
|
||||||
|
* @return A unique channel handler name.
|
||||||
|
*/
|
||||||
|
protected String getHandlerName() {
|
||||||
|
return "tiny-" + plugin.getName() + "-" + ID.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets.
|
||||||
|
* <p>
|
||||||
|
* This will automatically be called when a player has logged in.
|
||||||
|
*
|
||||||
|
* @param player - the player to inject.
|
||||||
|
*/
|
||||||
|
public void injectPlayer(Player player) {
|
||||||
|
injectChannelInternal(getChannel(player)).player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a custom channel handler to the given channel.
|
||||||
|
*
|
||||||
|
* @param channel - the channel to inject.
|
||||||
|
*/
|
||||||
|
public void injectChannel(Channel channel) {
|
||||||
|
injectChannelInternal(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a custom channel handler to the given channel.
|
||||||
|
*
|
||||||
|
* @param channel - the channel to inject.
|
||||||
|
* @return The packet interceptor.
|
||||||
|
*/
|
||||||
|
private PacketInterceptor injectChannelInternal(Channel channel) {
|
||||||
|
try {
|
||||||
|
PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName);
|
||||||
|
|
||||||
|
// Inject our packet interceptor
|
||||||
|
if (interceptor == null) {
|
||||||
|
interceptor = new PacketInterceptor();
|
||||||
|
channel.pipeline().addBefore("packet_handler", handlerName, interceptor);
|
||||||
|
uninjectedChannels.remove(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return interceptor;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Try again
|
||||||
|
return (PacketInterceptor) channel.pipeline().get(handlerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Netty channel associated with a player. This is cached.
|
||||||
|
*
|
||||||
|
* @param player - the player.
|
||||||
|
* @return The Netty channel.
|
||||||
|
*/
|
||||||
|
public Channel getChannel(Player player) {
|
||||||
|
Channel channel = channelLookup.get(player.getName());
|
||||||
|
|
||||||
|
// Lookup channel again
|
||||||
|
if (channel == null) {
|
||||||
|
Object connection = getConnection.get(getPlayerHandle.invoke(player));
|
||||||
|
Object manager = getManager.get(connection);
|
||||||
|
|
||||||
|
channelLookup.put(player.getName(), channel = getChannel.get(manager));
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninject a specific player.
|
||||||
|
*
|
||||||
|
* @param player - the injected player.
|
||||||
|
*/
|
||||||
|
public void uninjectPlayer(Player player) {
|
||||||
|
uninjectChannel(getChannel(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninject a specific channel.
|
||||||
|
* <p>
|
||||||
|
* This will also disable the automatic channel injection that occurs when a player has properly logged in.
|
||||||
|
*
|
||||||
|
* @param channel - the injected channel.
|
||||||
|
*/
|
||||||
|
public void uninjectChannel(final Channel channel) {
|
||||||
|
// No need to guard against this if we're closing
|
||||||
|
if (!closed) {
|
||||||
|
uninjectedChannels.add(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See ChannelInjector in ProtocolLib, line 590
|
||||||
|
channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given player has been injected by TinyProtocol.
|
||||||
|
*
|
||||||
|
* @param player - the player.
|
||||||
|
* @return TRUE if it is, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasInjected(Player player) {
|
||||||
|
return hasInjected(getChannel(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given channel has been injected by TinyProtocol.
|
||||||
|
*
|
||||||
|
* @param channel - the channel.
|
||||||
|
* @return TRUE if it is, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasInjected(Channel channel) {
|
||||||
|
return channel.pipeline().get(handlerName) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cease listening for packets. This is called automatically when your plugin is disabled.
|
||||||
|
*/
|
||||||
|
public final void close() {
|
||||||
|
if (!closed) {
|
||||||
|
closed = true;
|
||||||
|
|
||||||
|
// Remove our handlers
|
||||||
|
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||||
|
uninjectPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up Bukkit
|
||||||
|
HandlerList.unregisterAll(listener);
|
||||||
|
unregisterChannelHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
private final class PacketInterceptor extends ChannelDuplexHandler {
|
||||||
|
// Updated by the login event
|
||||||
|
public volatile Player player;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
|
// Intercept channel
|
||||||
|
final Channel channel = ctx.channel();
|
||||||
|
handleLoginStart(channel, msg);
|
||||||
|
|
||||||
|
try {
|
||||||
|
msg = onPacketInAsync(player, channel, msg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg != null) {
|
||||||
|
super.channelRead(ctx, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
|
try {
|
||||||
|
msg = onPacketOutAsync(player, ctx.channel(), msg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg != null) {
|
||||||
|
super.write(ctx, msg, promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleLoginStart(Channel channel, Object packet) {
|
||||||
|
if (PACKET_LOGIN_IN_START.isInstance(packet)) {
|
||||||
|
GameProfile profile = getGameProfile.get(packet);
|
||||||
|
channelLookup.put(profile.getName(), channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.listeners.ChunkListener;
|
||||||
|
import net.jitse.npclib.listeners.PacketListener;
|
||||||
|
import net.jitse.npclib.listeners.PlayerListener;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPCLib {
|
||||||
|
|
||||||
|
private final Server server;
|
||||||
|
private final JavaPlugin plugin;
|
||||||
|
private final Class<?> npcClass;
|
||||||
|
|
||||||
|
public NPCLib(JavaPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.server = plugin.getServer();
|
||||||
|
|
||||||
|
String versionName = server.getClass().getPackage().getName().split("\\.")[3];
|
||||||
|
|
||||||
|
Class<?> npcClass = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
npcClass = Class.forName("net.jitse.npclib.nms." + versionName + ".NPC_" + versionName);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Version not supported, error below
|
||||||
|
}
|
||||||
|
|
||||||
|
this.npcClass = npcClass;
|
||||||
|
|
||||||
|
if (npcClass == null) {
|
||||||
|
server.getConsoleSender().sendMessage(ChatColor.RED + "NPCLib failed to initiate. Your server's version ("
|
||||||
|
+ versionName + ") is not supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.getConsoleSender().sendMessage(ChatColor.BLUE + "[NPCLib] " + ChatColor.WHITE + "Enabled for version " + versionName + ".");
|
||||||
|
|
||||||
|
registerInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerInternal() {
|
||||||
|
PluginManager pluginManager = server.getPluginManager();
|
||||||
|
|
||||||
|
pluginManager.registerEvents(new PlayerListener(), plugin);
|
||||||
|
pluginManager.registerEvents(new ChunkListener(), plugin);
|
||||||
|
|
||||||
|
new PacketListener().start(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new non-player character (NPC).
|
||||||
|
*
|
||||||
|
* @param skin The skin you want the NPC to have.
|
||||||
|
* @param autoHideDistance Distance from where you want to NPC to hide from the player (50 recommended).
|
||||||
|
* @param lines The text you want to sendShowPackets above the NPC (null = no text).
|
||||||
|
* @return The NPC object you may use to sendShowPackets it to players.
|
||||||
|
*/
|
||||||
|
public NPC createNPC(Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
try {
|
||||||
|
return (NPC) npcClass.getConstructors()[0].newInstance(plugin, skin, autoHideDistance, lines);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
server.getConsoleSender().sendMessage(ChatColor.RED + "NPCLib failed to create NPC. Please report this stacktrace:");
|
||||||
|
exception.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new non-player character (NPC).
|
||||||
|
*
|
||||||
|
* @param skin The skin you want the NPC to have.
|
||||||
|
* @param lines The text you want to sendShowPackets above the NPC (null = no text).
|
||||||
|
* @return The NPC object you may use to sendShowPackets it to players.
|
||||||
|
*/
|
||||||
|
public NPC createNPC(Skin skin, List<String> lines) {
|
||||||
|
return createNPC(skin, 50, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new non-player character (NPC).
|
||||||
|
*
|
||||||
|
* @param skin The skin you want the NPC to have.
|
||||||
|
* @return The NPC object you may use to sendShowPackets it to players.
|
||||||
|
*/
|
||||||
|
public NPC createNPC(Skin skin) {
|
||||||
|
return createNPC(skin, 50, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new non-player character (NPC).
|
||||||
|
*
|
||||||
|
* @return The NPC object you may use to sendShowPackets it to players.
|
||||||
|
*/
|
||||||
|
public NPC createNPC() {
|
||||||
|
return createNPC(null, 50, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public final class NPCManager {
|
||||||
|
|
||||||
|
private static Set<NPC> npcs = new HashSet<>();
|
||||||
|
|
||||||
|
public static Set<NPC> getAllNPCs() {
|
||||||
|
return npcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void add(NPC npc) {
|
||||||
|
npcs.add(npc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void remove(NPC npc) {
|
||||||
|
npcs.remove(npc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NPCManager() {
|
||||||
|
throw new SecurityException("You cannot initialize this class.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.api;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.authlib.properties.Property;
|
||||||
|
import net.jitse.npclib.NPCManager;
|
||||||
|
import net.jitse.npclib.events.NPCDestroyEvent;
|
||||||
|
import net.jitse.npclib.events.NPCSpawnEvent;
|
||||||
|
import net.jitse.npclib.events.trigger.TriggerType;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public abstract class NPC {
|
||||||
|
|
||||||
|
protected final UUID uuid = UUID.randomUUID();
|
||||||
|
protected final String name = uuid.toString().replace("-", "").substring(0, 10);
|
||||||
|
protected final int entityId = (int) Math.ceil(Math.random() * 100000) + 100000;
|
||||||
|
|
||||||
|
private final Set<UUID> shown = new HashSet<>();
|
||||||
|
private final Set<UUID> autoHidden = new HashSet<>();
|
||||||
|
|
||||||
|
protected final double autoHideDistance;
|
||||||
|
protected final Skin skin;
|
||||||
|
protected final List<String> lines;
|
||||||
|
|
||||||
|
protected JavaPlugin plugin;
|
||||||
|
protected GameProfile gameProfile;
|
||||||
|
protected Location location;
|
||||||
|
|
||||||
|
public NPC(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.skin = skin;
|
||||||
|
this.autoHideDistance = autoHideDistance;
|
||||||
|
this.lines = (lines == null ? Collections.emptyList() : lines);
|
||||||
|
|
||||||
|
NPCManager.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GameProfile generateGameProfile(UUID uuid, String name) {
|
||||||
|
GameProfile gameProfile = new GameProfile(uuid, name);
|
||||||
|
|
||||||
|
if (skin != null) {
|
||||||
|
gameProfile.getProperties().put("textures", new Property("textures", skin.getValue(), skin.getSignature()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return gameProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
NPCManager.remove(this);
|
||||||
|
|
||||||
|
// Destroy NPC for every player that is still seeing it.
|
||||||
|
for (UUID uuid : shown) {
|
||||||
|
if (autoHidden.contains(uuid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide(Bukkit.getPlayer(uuid), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UUID> getShown() {
|
||||||
|
return shown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UUID> getAutoHidden() {
|
||||||
|
return autoHidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location getLocation() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAutoHideDistance() {
|
||||||
|
return autoHideDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEntityId() {
|
||||||
|
return entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActuallyShown(Player player) {
|
||||||
|
return shown.contains(player.getUniqueId()) && !autoHidden.contains(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate packets.
|
||||||
|
public abstract void create(Location location);
|
||||||
|
|
||||||
|
public void show(Player player) {
|
||||||
|
show(player, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(Player player, boolean auto) {
|
||||||
|
NPCSpawnEvent event = new NPCSpawnEvent(this, player, auto ? TriggerType.AUTOMATIC : TriggerType.MANUAL);
|
||||||
|
plugin.getServer().getPluginManager().callEvent(event);
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto) {
|
||||||
|
sendShowPackets(player);
|
||||||
|
} else {
|
||||||
|
if (shown.contains(player.getUniqueId())) {
|
||||||
|
throw new RuntimeException("Cannot call show method twice.");
|
||||||
|
}
|
||||||
|
|
||||||
|
shown.add(player.getUniqueId());
|
||||||
|
|
||||||
|
if (player.getLocation().distance(location) <= autoHideDistance) {
|
||||||
|
sendShowPackets(player);
|
||||||
|
} else {
|
||||||
|
if (!autoHidden.contains(player.getUniqueId())) {
|
||||||
|
autoHidden.add(player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal method.
|
||||||
|
protected abstract void sendShowPackets(Player player);
|
||||||
|
|
||||||
|
public void hide(Player player) {
|
||||||
|
hide(player, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hide(Player player, boolean auto) {
|
||||||
|
NPCDestroyEvent event = new NPCDestroyEvent(this, player, auto ? TriggerType.AUTOMATIC : TriggerType.MANUAL);
|
||||||
|
plugin.getServer().getPluginManager().callEvent(event);
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto) {
|
||||||
|
sendHidePackets(player);
|
||||||
|
} else {
|
||||||
|
if (!shown.contains(player.getUniqueId())) {
|
||||||
|
throw new RuntimeException("Cannot call hide method without calling NPC#show.");
|
||||||
|
}
|
||||||
|
|
||||||
|
shown.remove(player.getUniqueId());
|
||||||
|
|
||||||
|
if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location) <= autoHideDistance) {
|
||||||
|
sendHidePackets(player);
|
||||||
|
} else {
|
||||||
|
if (autoHidden.contains(player.getUniqueId())) {
|
||||||
|
autoHidden.remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal method.
|
||||||
|
protected abstract void sendHidePackets(Player player);
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.events;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.events.trigger.TriggerType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPCDestroyEvent extends Event implements Cancellable {
|
||||||
|
|
||||||
|
private static final HandlerList handlers = new HandlerList();
|
||||||
|
|
||||||
|
private boolean cancelled = false;
|
||||||
|
|
||||||
|
private final NPC npc;
|
||||||
|
private final Player player;
|
||||||
|
private final TriggerType trigger;
|
||||||
|
|
||||||
|
public NPCDestroyEvent(NPC npc, Player player, TriggerType trigger) {
|
||||||
|
this.npc = npc;
|
||||||
|
this.player = player;
|
||||||
|
this.trigger = trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCancelled(boolean cancelled) {
|
||||||
|
this.cancelled = cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NPC getNPC() {
|
||||||
|
return npc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerType getTrigger() {
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandlerList getHandlers() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.events;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.events.click.ClickType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPCInteractEvent extends Event {
|
||||||
|
|
||||||
|
private static final HandlerList handlers = new HandlerList();
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
private final ClickType clickType;
|
||||||
|
private final NPC npc;
|
||||||
|
|
||||||
|
public NPCInteractEvent(Player player, ClickType clickType, NPC npc) {
|
||||||
|
this.player = player;
|
||||||
|
this.clickType = clickType;
|
||||||
|
this.npc = npc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getWhoClicked() {
|
||||||
|
return this.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClickType getClickType() {
|
||||||
|
return this.clickType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NPC getNPC() {
|
||||||
|
return this.npc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandlerList getHandlers() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.events;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.events.trigger.TriggerType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPCSpawnEvent extends Event implements Cancellable {
|
||||||
|
|
||||||
|
private static final HandlerList handlers = new HandlerList();
|
||||||
|
|
||||||
|
private boolean cancelled = false;
|
||||||
|
|
||||||
|
private final NPC npc;
|
||||||
|
private final Player player;
|
||||||
|
private final TriggerType trigger;
|
||||||
|
|
||||||
|
public NPCSpawnEvent(NPC npc, Player player, TriggerType trigger) {
|
||||||
|
this.npc = npc;
|
||||||
|
this.player = player;
|
||||||
|
this.trigger = trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCancelled(boolean cancelled) {
|
||||||
|
this.cancelled = cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NPC getNPC() {
|
||||||
|
return npc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerType getTrigger() {
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandlerList getHandlers() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.events.click;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public enum ClickType {
|
||||||
|
|
||||||
|
LEFT_CLICK, RIGHT_CLICK
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package net.jitse.npclib.events.trigger;
|
||||||
|
|
||||||
|
public enum TriggerType {
|
||||||
|
|
||||||
|
MANUAL, AUTOMATIC
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.listeners;
|
||||||
|
|
||||||
|
import net.jitse.npclib.NPCManager;
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.world.ChunkLoadEvent;
|
||||||
|
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstras
|
||||||
|
*/
|
||||||
|
public class ChunkListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onChunkUnload(ChunkUnloadEvent event) {
|
||||||
|
Chunk chunk = event.getChunk();
|
||||||
|
|
||||||
|
for (NPC npc : NPCManager.getAllNPCs()) {
|
||||||
|
Chunk npcChunk = npc.getLocation().getChunk();
|
||||||
|
|
||||||
|
if (chunk.equals(npcChunk)) {
|
||||||
|
// Unloaded chunk with NPC in it. Hiding it from all players currently shown to.
|
||||||
|
|
||||||
|
for (UUID uuid : npc.getShown()) {
|
||||||
|
// Safety check so it doesn't send packets if the NPC has already
|
||||||
|
// been automatically despawned by the system.
|
||||||
|
if (npc.getAutoHidden().contains(uuid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
npc.hide(Bukkit.getPlayer(uuid), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onChunkLoad(ChunkLoadEvent event) {
|
||||||
|
Chunk chunk = event.getChunk();
|
||||||
|
|
||||||
|
for (NPC npc : NPCManager.getAllNPCs()) {
|
||||||
|
Chunk npcChunk = npc.getLocation().getChunk();
|
||||||
|
|
||||||
|
if (chunk.equals(npcChunk)) {
|
||||||
|
// Loaded chunk with NPC in it. Showing it to the players again.
|
||||||
|
|
||||||
|
for (UUID uuid : npc.getShown()) {
|
||||||
|
// Make sure not to respawn a not-hidden NPC.
|
||||||
|
if (!npc.getAutoHidden().contains(uuid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player player = Bukkit.getPlayer(uuid);
|
||||||
|
|
||||||
|
if (!npcChunk.getWorld().equals(player.getWorld())) {
|
||||||
|
continue; // Player and NPC are not in the same world.
|
||||||
|
}
|
||||||
|
|
||||||
|
double hideDistance = npc.getAutoHideDistance();
|
||||||
|
double distanceSquared = player.getLocation().distanceSquared(npc.getLocation());
|
||||||
|
boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4);
|
||||||
|
|
||||||
|
// Show the NPC (if in range).
|
||||||
|
if (inRange) {
|
||||||
|
npc.show(Bukkit.getPlayer(uuid), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.listeners;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import net.jitse.npclib.NPCManager;
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.events.NPCInteractEvent;
|
||||||
|
import net.jitse.npclib.events.click.ClickType;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketListener {
|
||||||
|
|
||||||
|
// Classes:
|
||||||
|
private final Class<?> packetPlayInUseEntityClazz = Reflection.getMinecraftClass("PacketPlayInUseEntity");
|
||||||
|
|
||||||
|
// Fields:
|
||||||
|
private final Reflection.FieldAccessor entityIdField = Reflection.getField(packetPlayInUseEntityClazz, "a", int.class);
|
||||||
|
private final Reflection.FieldAccessor actionField = Reflection.getField(packetPlayInUseEntityClazz, "action", Object.class);
|
||||||
|
|
||||||
|
// Prevent players from clicking at very high speeds.
|
||||||
|
private final Set<UUID> delay = new HashSet<>();
|
||||||
|
|
||||||
|
public void start(JavaPlugin plugin) {
|
||||||
|
new TinyProtocol(plugin) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object onPacketInAsync(Player player, Channel channel, Object packet) {
|
||||||
|
|
||||||
|
if (packetPlayInUseEntityClazz.isInstance(packet)) {
|
||||||
|
NPC npc = NPCManager.getAllNPCs().stream().filter(
|
||||||
|
check -> check.isActuallyShown(player) && check.getEntityId() == (int) entityIdField.get(packet))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
|
||||||
|
if (npc == null) {
|
||||||
|
// Default player, not doing magic with the packet.
|
||||||
|
return super.onPacketInAsync(player, channel, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delay.contains(player.getUniqueId())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClickType clickType = actionField.get(packet).toString()
|
||||||
|
.equals("ATTACK") ? ClickType.LEFT_CLICK : ClickType.RIGHT_CLICK;
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc));
|
||||||
|
|
||||||
|
UUID uuid = player.getUniqueId();
|
||||||
|
delay.add(uuid);
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () -> delay.remove(uuid), 1);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return super.onPacketInAsync(player, channel, packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.listeners;
|
||||||
|
|
||||||
|
import net.jitse.npclib.NPCManager;
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||||
|
import org.bukkit.event.player.PlayerMoveEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PlayerListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
for (NPC npc : NPCManager.getAllNPCs()) {
|
||||||
|
if (npc.getAutoHidden().contains(player.getUniqueId())) {
|
||||||
|
npc.getAutoHidden().remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't need to use NPC#hide since the entity is not registered in the NMS server.
|
||||||
|
if (npc.getShown().contains(player.getUniqueId())) {
|
||||||
|
npc.getShown().remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerChangedWorld(PlayerChangedWorldEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
World from = event.getFrom();
|
||||||
|
|
||||||
|
// The PlayerTeleportEvent is call, and will handle visibility in the new world.
|
||||||
|
for (NPC npc : NPCManager.getAllNPCs()) {
|
||||||
|
if (npc.getLocation().getWorld().equals(from)) {
|
||||||
|
if (!npc.getAutoHidden().contains(player.getUniqueId())) {
|
||||||
|
npc.getAutoHidden().add(player.getUniqueId());
|
||||||
|
npc.hide(player, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerMove(PlayerMoveEvent event) {
|
||||||
|
Location from = event.getFrom();
|
||||||
|
Location to = event.getTo();
|
||||||
|
|
||||||
|
if (from.getX() == to.getX() && from.getY() == to.getY() && from.getZ() == to.getZ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMove(event.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerTeleport(PlayerTeleportEvent event) {
|
||||||
|
handleMove(event.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleMove(Player player) {
|
||||||
|
World world = player.getWorld();
|
||||||
|
for (NPC npc : NPCManager.getAllNPCs()) {
|
||||||
|
if (!npc.getShown().contains(player.getUniqueId())) {
|
||||||
|
continue; // NPC was never supposed to be shown to the player.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!npc.getLocation().getWorld().equals(world)) {
|
||||||
|
continue; // NPC is not in the same world.
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Bukkit doesn't track the NPC entity anymore, bypass the hiding distance variable.
|
||||||
|
// This will cause issues otherwise (e.g. custom skin disappearing).
|
||||||
|
double hideDistance = npc.getAutoHideDistance();
|
||||||
|
double distanceSquared = player.getLocation().distanceSquared(npc.getLocation());
|
||||||
|
boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4);
|
||||||
|
if (npc.getAutoHidden().contains(player.getUniqueId())) {
|
||||||
|
// Check if the player and NPC are within the range to sendShowPackets it again.
|
||||||
|
if (inRange) {
|
||||||
|
npc.show(player, true);
|
||||||
|
npc.getAutoHidden().remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check if the player and NPC are out of range to sendHidePackets it.
|
||||||
|
if (!inRange) {
|
||||||
|
npc.hide(player, true);
|
||||||
|
npc.getAutoHidden().add(player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.holograms;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class Hologram {
|
||||||
|
|
||||||
|
private final double delta = 0.3;
|
||||||
|
|
||||||
|
private List<Object> armorStands = new ArrayList<>();
|
||||||
|
private Set<Object> spawnPackets = new HashSet<>();
|
||||||
|
private Set<Object> destroyPackets = new HashSet<>();
|
||||||
|
|
||||||
|
// Classes:
|
||||||
|
private static final Class<?> ENTITY_ARMOR_STAND_CLAZZ = Reflection.getMinecraftClass("EntityArmorStand");
|
||||||
|
private static final Class<?> ENTITY_LIVING_CLAZZ = Reflection.getMinecraftClass("EntityLiving");
|
||||||
|
private static final Class<?> ENTITY_CLAZZ = Reflection.getMinecraftClass("Entity");
|
||||||
|
private static final Class<?> CRAFT_BUKKIT_CLASS = Reflection.getCraftBukkitClass("CraftWorld");
|
||||||
|
private static final Class<?> CRAFT_PLAYER_CLAZZ = Reflection.getCraftBukkitClass("entity.CraftPlayer");
|
||||||
|
private static final Class<?> PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CLAZZ = Reflection.getMinecraftClass(
|
||||||
|
"PacketPlayOutSpawnEntityLiving");
|
||||||
|
private static final Class<?> PACKET_PLAY_OUT_ENTITY_DESTROY_CLAZZ = Reflection.getMinecraftClass(
|
||||||
|
"PacketPlayOutEntityDestroy");
|
||||||
|
private static final Class<?> ENTITY_PLAYER_CLAZZ = Reflection.getMinecraftClass("EntityPlayer");
|
||||||
|
private static final Class<?> PLAYER_CONNECTION_CLAZZ = Reflection.getMinecraftClass("PlayerConnection");
|
||||||
|
private static final Class<?> PACKET_CLAZZ = Reflection.getMinecraftClass("Packet");
|
||||||
|
|
||||||
|
// Constructors:
|
||||||
|
private static final Reflection.ConstructorInvoker PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CONSTRUCTOR = Reflection
|
||||||
|
.getConstructor(PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CLAZZ, ENTITY_LIVING_CLAZZ);
|
||||||
|
private static final Reflection.ConstructorInvoker PACKET_PLAY_OUT_ENTITY_DESTROY_CONSTRUCTOR = Reflection
|
||||||
|
.getConstructor(PACKET_PLAY_OUT_ENTITY_DESTROY_CLAZZ, int[].class);
|
||||||
|
|
||||||
|
// Fields:
|
||||||
|
private static final Reflection.FieldAccessor playerConnectionField = Reflection.getField(ENTITY_PLAYER_CLAZZ,
|
||||||
|
"playerConnection", PLAYER_CONNECTION_CLAZZ);
|
||||||
|
|
||||||
|
// Methods:
|
||||||
|
private static final Reflection.MethodInvoker SET_LOCATION_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"setLocation", double.class, double.class, double.class, float.class, float.class);
|
||||||
|
private static final Reflection.MethodInvoker SET_CUSTOM_NAME_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"setCustomName", String.class);
|
||||||
|
private static final Reflection.MethodInvoker SET_CUSTOM_NAME_VISIBLE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"setCustomNameVisible", boolean.class);
|
||||||
|
private static final Reflection.MethodInvoker SET_SMALL_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"setSmall", boolean.class);
|
||||||
|
private static final Reflection.MethodInvoker SET_INVISIBLE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"setInvisible", boolean.class);
|
||||||
|
private static final Reflection.MethodInvoker SET_BASE_PLATE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"setBasePlate", boolean.class);
|
||||||
|
private static final Reflection.MethodInvoker SET_ARMS_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"setArms", boolean.class);
|
||||||
|
private static final Reflection.MethodInvoker PLAYER_GET_HANDLE_METHOD = Reflection.getMethod(CRAFT_PLAYER_CLAZZ,
|
||||||
|
"getHandle");
|
||||||
|
private static final Reflection.MethodInvoker SEND_PACKET_METHOD = Reflection.getMethod(PLAYER_CONNECTION_CLAZZ,
|
||||||
|
"sendPacket", PACKET_CLAZZ);
|
||||||
|
private static final Reflection.MethodInvoker GET_ID_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"getId");
|
||||||
|
|
||||||
|
private final Location start;
|
||||||
|
private final List<String> lines;
|
||||||
|
private final Object worldServer;
|
||||||
|
|
||||||
|
public Hologram(Location location, List<String> lines) {
|
||||||
|
this.start = location;
|
||||||
|
this.lines = lines;
|
||||||
|
|
||||||
|
this.worldServer = Reflection.getMethod(CRAFT_BUKKIT_CLASS, "getHandle")
|
||||||
|
.invoke(CRAFT_BUKKIT_CLASS.cast(location.getWorld()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generatePackets(boolean above1_9_r2) {
|
||||||
|
Reflection.MethodInvoker gravityMethod = (above1_9_r2 ? Reflection.getMethod(ENTITY_CLAZZ,
|
||||||
|
"setNoGravity", boolean.class) : Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
|
"setGravity", boolean.class));
|
||||||
|
|
||||||
|
Location location = start.clone().add(0, delta * lines.size(), 0);
|
||||||
|
Class<?> worldClass = worldServer.getClass().getSuperclass();
|
||||||
|
|
||||||
|
if (start.getWorld().getEnvironment() != World.Environment.NORMAL) {
|
||||||
|
worldClass = worldClass.getSuperclass();
|
||||||
|
}
|
||||||
|
|
||||||
|
Reflection.ConstructorInvoker entityArmorStandConstructor = Reflection
|
||||||
|
.getConstructor(ENTITY_ARMOR_STAND_CLAZZ, worldClass);
|
||||||
|
|
||||||
|
for (String line : lines) {
|
||||||
|
Object entityArmorStand = entityArmorStandConstructor.invoke(worldServer);
|
||||||
|
|
||||||
|
SET_LOCATION_METHOD.invoke(entityArmorStand, location.getX(), location.getY(), location.getZ(), 0, 0);
|
||||||
|
SET_CUSTOM_NAME_METHOD.invoke(entityArmorStand, line);
|
||||||
|
SET_CUSTOM_NAME_VISIBLE_METHOD.invoke(entityArmorStand, true);
|
||||||
|
gravityMethod.invoke(entityArmorStand, above1_9_r2);
|
||||||
|
SET_SMALL_METHOD.invoke(entityArmorStand, true);
|
||||||
|
SET_INVISIBLE_METHOD.invoke(entityArmorStand, true);
|
||||||
|
SET_BASE_PLATE_METHOD.invoke(entityArmorStand, false);
|
||||||
|
SET_ARMS_METHOD.invoke(entityArmorStand, false);
|
||||||
|
|
||||||
|
location.subtract(0, delta, 0);
|
||||||
|
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
armorStands.add(entityArmorStand);
|
||||||
|
|
||||||
|
Object spawnPacket = PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CONSTRUCTOR.invoke(entityArmorStand);
|
||||||
|
spawnPackets.add(spawnPacket);
|
||||||
|
|
||||||
|
Object destroyPacket = PACKET_PLAY_OUT_ENTITY_DESTROY_CONSTRUCTOR
|
||||||
|
.invoke(new int[]{(int) GET_ID_METHOD.invoke(entityArmorStand)});
|
||||||
|
destroyPackets.add(destroyPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spawn(Player player) {
|
||||||
|
Object playerConnection = playerConnectionField.get(PLAYER_GET_HANDLE_METHOD
|
||||||
|
.invoke(CRAFT_PLAYER_CLAZZ.cast(player)));
|
||||||
|
|
||||||
|
for (Object packet : spawnPackets) {
|
||||||
|
SEND_PACKET_METHOD.invoke(playerConnection, packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy(Player player) {
|
||||||
|
Object playerConnection = playerConnectionField.get(PLAYER_GET_HANDLE_METHOD
|
||||||
|
.invoke(CRAFT_PLAYER_CLAZZ.cast(player)));
|
||||||
|
|
||||||
|
for (Object packet : destroyPackets) {
|
||||||
|
SEND_PACKET_METHOD.invoke(playerConnection, packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.skin;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class MineSkinFetcher {
|
||||||
|
|
||||||
|
private static final String MINESKIN_API = "https://api.mineskin.org/get/id/";
|
||||||
|
|
||||||
|
public static void fetchSkinFromIdAsync(int id, Callback callback) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(MINESKIN_API + id).openConnection();
|
||||||
|
httpURLConnection.setRequestMethod("GET");
|
||||||
|
httpURLConnection.setDoOutput(true);
|
||||||
|
httpURLConnection.setDoInput(true);
|
||||||
|
httpURLConnection.connect();
|
||||||
|
|
||||||
|
Scanner scanner = new Scanner(httpURLConnection.getInputStream());
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
builder.append(scanner.nextLine());
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner.close();
|
||||||
|
httpURLConnection.disconnect();
|
||||||
|
|
||||||
|
JsonObject jsonObject = (JsonObject) new JsonParser().parse(builder.toString());
|
||||||
|
JsonObject textures = jsonObject.get("data").getAsJsonObject().get("texture").getAsJsonObject();
|
||||||
|
String value = textures.get("value").getAsString();
|
||||||
|
String signature = textures.get("signature").getAsString();
|
||||||
|
|
||||||
|
callback.call(new Skin(value, signature));
|
||||||
|
} catch (IOException exception) {
|
||||||
|
Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Could not fetch skin! (Id: " + id + "). Message: " + exception.getMessage());
|
||||||
|
exception.printStackTrace();
|
||||||
|
callback.failed();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
|
||||||
|
void call(Skin skinData);
|
||||||
|
|
||||||
|
default void failed() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.skin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class Skin {
|
||||||
|
|
||||||
|
private final String value, signature;
|
||||||
|
|
||||||
|
public Skin(String value, String signature) {
|
||||||
|
this.value = value;
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignature() {
|
||||||
|
return this.signature;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>v1_8_R1</module>
|
||||||
|
<module>v1_8_R2</module>
|
||||||
|
<module>v1_8_R3</module>
|
||||||
|
<module>v1_9_R1</module>
|
||||||
|
<module>v1_9_R2</module>
|
||||||
|
<module>v1_10_R1</module>
|
||||||
|
<module>v1_11_R1</module>
|
||||||
|
<module>v1_12_R1</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-commons</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms-v1_10_R1</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.10.2-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_10_R1;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.nms.holograms.Hologram;
|
||||||
|
import net.jitse.npclib.nms.v1_10_R1.packets.PacketPlayOutEntityHeadRotationWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_10_R1.packets.PacketPlayOutNamedEntitySpawnWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_10_R1.packets.PacketPlayOutPlayerInfoWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_10_R1.packets.PacketPlayOutScoreboardTeamWrapper;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import net.minecraft.server.v1_10_R1.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_10_R1.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPC_v1_10_R1 extends NPC {
|
||||||
|
|
||||||
|
private Hologram hologram;
|
||||||
|
private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn;
|
||||||
|
private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister;
|
||||||
|
private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove;
|
||||||
|
private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation;
|
||||||
|
private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy;
|
||||||
|
|
||||||
|
public NPC_v1_10_R1(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
super(plugin, skin, autoHideDistance, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
this.hologram = new Hologram(location.clone().subtract(0, 0.5, 0), lines);
|
||||||
|
hologram.generatePackets(true);
|
||||||
|
|
||||||
|
this.gameProfile = generateGameProfile(uuid, name);
|
||||||
|
PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper();
|
||||||
|
|
||||||
|
// Packets for spawning the NPC:
|
||||||
|
this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createRegisterTeam(name); // First packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper()
|
||||||
|
.create(uuid, location, entityId); // Third packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper()
|
||||||
|
.create(location, entityId); // Fourth packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed).
|
||||||
|
|
||||||
|
// Packet for destroying the NPC:
|
||||||
|
this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send.
|
||||||
|
|
||||||
|
// Second packet to send is "packetPlayOutPlayerInfoRemove".
|
||||||
|
|
||||||
|
this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createUnregisterTeam(name); // Third packet to send.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendShowPackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoAdd);
|
||||||
|
playerConnection.sendPacket(packetPlayOutNamedEntitySpawn);
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityHeadRotation);
|
||||||
|
|
||||||
|
hologram.spawn(player);
|
||||||
|
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHidePackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityDestroy);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove);
|
||||||
|
|
||||||
|
hologram.destroy(player);
|
||||||
|
|
||||||
|
// Sending this a bit later so the player doesn't see the name (for that split second).
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_10_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_10_R1.PacketPlayOutEntityHeadRotation;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutEntityHeadRotationWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutEntityHeadRotation create(Location location, int entityId) {
|
||||||
|
PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class).
|
||||||
|
set(packetPlayOutEntityHeadRotation, entityId);
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class)
|
||||||
|
.set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F));
|
||||||
|
|
||||||
|
return packetPlayOutEntityHeadRotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_10_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_10_R1.DataWatcher;
|
||||||
|
import net.minecraft.server.v1_10_R1.DataWatcherObject;
|
||||||
|
import net.minecraft.server.v1_10_R1.DataWatcherRegistry;
|
||||||
|
import net.minecraft.server.v1_10_R1.PacketPlayOutNamedEntitySpawn;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutNamedEntitySpawnWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) {
|
||||||
|
PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, entityId);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, uuid);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getX());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getY());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getZ());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F)));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F)));
|
||||||
|
|
||||||
|
DataWatcher dataWatcher = new DataWatcher(null);
|
||||||
|
dataWatcher.register(new DataWatcherObject<>(13, DataWatcherRegistry.a), (byte) 127);
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "h", DataWatcher.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, dataWatcher);
|
||||||
|
|
||||||
|
return packetPlayOutNamedEntitySpawn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_10_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.server.v1_10_R1.EnumGamemode;
|
||||||
|
import net.minecraft.server.v1_10_R1.IChatBaseComponent;
|
||||||
|
import net.minecraft.server.v1_10_R1.PacketPlayOutPlayerInfo;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutPlayerInfoWrapper {
|
||||||
|
|
||||||
|
private final Class<?> packetPlayOutPlayerInfoClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo");
|
||||||
|
private final Class<?> playerInfoDataClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo$PlayerInfoData");
|
||||||
|
private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz,
|
||||||
|
packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class);
|
||||||
|
|
||||||
|
public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) {
|
||||||
|
PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo();
|
||||||
|
Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class)
|
||||||
|
.set(packetPlayOutPlayerInfo, action);
|
||||||
|
|
||||||
|
Object playerInfoData = playerInfoDataConstructor.invoke(packetPlayOutPlayerInfo,
|
||||||
|
gameProfile, 1, EnumGamemode.NOT_SET,
|
||||||
|
IChatBaseComponent.ChatSerializer.b("{\"text\":\"" + ChatColor.BLUE + "[NPC] " + name + "\"}")
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflection.FieldAccessor<List> fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(), "b", List.class);
|
||||||
|
List list = fieldAccessor.get(packetPlayOutPlayerInfo);
|
||||||
|
list.add(playerInfoData);
|
||||||
|
fieldAccessor.set(packetPlayOutPlayerInfo, list);
|
||||||
|
|
||||||
|
return packetPlayOutPlayerInfo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_10_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_10_R1.PacketPlayOutScoreboardTeam;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutScoreboardTeamWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createRegisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "f", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.FieldAccessor<Collection> collectionFieldAccessor = Reflection.getField(
|
||||||
|
packetPlayOutScoreboardTeam.getClass(), "h", Collection.class);
|
||||||
|
Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam);
|
||||||
|
collection.add(name);
|
||||||
|
collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms-v1_11_R1</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.11.2-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_11_R1;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.nms.holograms.Hologram;
|
||||||
|
import net.jitse.npclib.nms.v1_11_R1.packets.PacketPlayOutEntityHeadRotationWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_11_R1.packets.PacketPlayOutNamedEntitySpawnWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_11_R1.packets.PacketPlayOutPlayerInfoWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_11_R1.packets.PacketPlayOutScoreboardTeamWrapper;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import net.minecraft.server.v1_11_R1.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_11_R1.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPC_v1_11_R1 extends NPC {
|
||||||
|
|
||||||
|
private Hologram hologram;
|
||||||
|
private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn;
|
||||||
|
private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister;
|
||||||
|
private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove;
|
||||||
|
private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation;
|
||||||
|
private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy;
|
||||||
|
|
||||||
|
public NPC_v1_11_R1(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
super(plugin, skin, autoHideDistance, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
this.hologram = new Hologram(location.clone().add(0, 0.5, 0), lines);
|
||||||
|
hologram.generatePackets(true);
|
||||||
|
|
||||||
|
this.gameProfile = generateGameProfile(uuid, name);
|
||||||
|
PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper();
|
||||||
|
|
||||||
|
// Packets for spawning the NPC:
|
||||||
|
this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createRegisterTeam(name); // First packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper()
|
||||||
|
.create(uuid, location, entityId); // Third packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper()
|
||||||
|
.create(location, entityId); // Fourth packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed).
|
||||||
|
|
||||||
|
// Packet for destroying the NPC:
|
||||||
|
this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send.
|
||||||
|
|
||||||
|
// Second packet to send is "packetPlayOutPlayerInfoRemove".
|
||||||
|
|
||||||
|
this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createUnregisterTeam(name); // Third packet to send.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendShowPackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoAdd);
|
||||||
|
playerConnection.sendPacket(packetPlayOutNamedEntitySpawn);
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityHeadRotation);
|
||||||
|
|
||||||
|
hologram.spawn(player);
|
||||||
|
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHidePackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityDestroy);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove);
|
||||||
|
|
||||||
|
hologram.destroy(player);
|
||||||
|
|
||||||
|
// Sending this a bit later so the player doesn't see the name (for that split second).
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_11_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_11_R1.PacketPlayOutEntityHeadRotation;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutEntityHeadRotationWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutEntityHeadRotation create(Location location, int entityId) {
|
||||||
|
PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class).
|
||||||
|
set(packetPlayOutEntityHeadRotation, entityId);
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class)
|
||||||
|
.set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F));
|
||||||
|
|
||||||
|
return packetPlayOutEntityHeadRotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_11_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_11_R1.DataWatcher;
|
||||||
|
import net.minecraft.server.v1_11_R1.DataWatcherObject;
|
||||||
|
import net.minecraft.server.v1_11_R1.DataWatcherRegistry;
|
||||||
|
import net.minecraft.server.v1_11_R1.PacketPlayOutNamedEntitySpawn;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutNamedEntitySpawnWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) {
|
||||||
|
PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, entityId);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, uuid);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getX());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getY());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getZ());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F)));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F)));
|
||||||
|
|
||||||
|
DataWatcher dataWatcher = new DataWatcher(null);
|
||||||
|
dataWatcher.register(new DataWatcherObject<>(13, DataWatcherRegistry.a), (byte) 127);
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "h", DataWatcher.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, dataWatcher);
|
||||||
|
|
||||||
|
return packetPlayOutNamedEntitySpawn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_11_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.server.v1_11_R1.EnumGamemode;
|
||||||
|
import net.minecraft.server.v1_11_R1.IChatBaseComponent;
|
||||||
|
import net.minecraft.server.v1_11_R1.PacketPlayOutPlayerInfo;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutPlayerInfoWrapper {
|
||||||
|
|
||||||
|
private final Class<?> packetPlayOutPlayerInfoClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo");
|
||||||
|
private final Class<?> playerInfoDataClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo$PlayerInfoData");
|
||||||
|
private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz,
|
||||||
|
packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class);
|
||||||
|
|
||||||
|
public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) {
|
||||||
|
PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo();
|
||||||
|
Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class)
|
||||||
|
.set(packetPlayOutPlayerInfo, action);
|
||||||
|
|
||||||
|
Object playerInfoData = playerInfoDataConstructor.invoke(packetPlayOutPlayerInfo,
|
||||||
|
gameProfile, 1, EnumGamemode.NOT_SET,
|
||||||
|
IChatBaseComponent.ChatSerializer.b("{\"text\":\"" + ChatColor.BLUE + "[NPC] " + name + "\"}")
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflection.FieldAccessor<List> fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(), "b", List.class);
|
||||||
|
List list = fieldAccessor.get(packetPlayOutPlayerInfo);
|
||||||
|
list.add(playerInfoData);
|
||||||
|
fieldAccessor.set(packetPlayOutPlayerInfo, list);
|
||||||
|
|
||||||
|
return packetPlayOutPlayerInfo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_11_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_11_R1.PacketPlayOutScoreboardTeam;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutScoreboardTeamWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createRegisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "f", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.FieldAccessor<Collection> collectionFieldAccessor = Reflection.getField(
|
||||||
|
packetPlayOutScoreboardTeam.getClass(), "h", Collection.class);
|
||||||
|
Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam);
|
||||||
|
collection.add(name);
|
||||||
|
collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms-v1_12_R1</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_12_R1;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.nms.holograms.Hologram;
|
||||||
|
import net.jitse.npclib.nms.v1_12_R1.packets.PacketPlayOutEntityHeadRotationWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_12_R1.packets.PacketPlayOutNamedEntitySpawnWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_12_R1.packets.PacketPlayOutPlayerInfoWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_12_R1.packets.PacketPlayOutScoreboardTeamWrapper;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import net.minecraft.server.v1_12_R1.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPC_v1_12_R1 extends NPC {
|
||||||
|
|
||||||
|
private Hologram hologram;
|
||||||
|
private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn;
|
||||||
|
private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister;
|
||||||
|
private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove;
|
||||||
|
private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation;
|
||||||
|
private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy;
|
||||||
|
|
||||||
|
public NPC_v1_12_R1(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
super(plugin, skin, autoHideDistance, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
this.hologram = new Hologram(location.clone().add(0, 0.5, 0), lines);
|
||||||
|
hologram.generatePackets(true);
|
||||||
|
|
||||||
|
this.gameProfile = generateGameProfile(uuid, name);
|
||||||
|
PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper();
|
||||||
|
|
||||||
|
// Packets for spawning the NPC:
|
||||||
|
this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createRegisterTeam(name); // First packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper()
|
||||||
|
.create(uuid, location, entityId); // Third packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper()
|
||||||
|
.create(location, entityId); // Fourth packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed).
|
||||||
|
|
||||||
|
// Packet for destroying the NPC:
|
||||||
|
this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send.
|
||||||
|
|
||||||
|
// Second packet to send is "packetPlayOutPlayerInfoRemove".
|
||||||
|
|
||||||
|
this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createUnregisterTeam(name); // Third packet to send.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendShowPackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoAdd);
|
||||||
|
playerConnection.sendPacket(packetPlayOutNamedEntitySpawn);
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityHeadRotation);
|
||||||
|
|
||||||
|
hologram.spawn(player);
|
||||||
|
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHidePackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityDestroy);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove);
|
||||||
|
|
||||||
|
hologram.destroy(player);
|
||||||
|
|
||||||
|
// Sending this a bit later so the player doesn't see the name (for that split second).
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_12_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_12_R1.PacketPlayOutEntityHeadRotation;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutEntityHeadRotationWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutEntityHeadRotation create(Location location, int entityId) {
|
||||||
|
PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class).
|
||||||
|
set(packetPlayOutEntityHeadRotation, entityId);
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class)
|
||||||
|
.set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F));
|
||||||
|
|
||||||
|
return packetPlayOutEntityHeadRotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_12_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_12_R1.DataWatcher;
|
||||||
|
import net.minecraft.server.v1_12_R1.DataWatcherObject;
|
||||||
|
import net.minecraft.server.v1_12_R1.DataWatcherRegistry;
|
||||||
|
import net.minecraft.server.v1_12_R1.PacketPlayOutNamedEntitySpawn;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutNamedEntitySpawnWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) {
|
||||||
|
PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, entityId);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, uuid);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getX());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getY());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getZ());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F)));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F)));
|
||||||
|
|
||||||
|
DataWatcher dataWatcher = new DataWatcher(null);
|
||||||
|
dataWatcher.register(new DataWatcherObject<>(13, DataWatcherRegistry.a), (byte) 127);
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "h", DataWatcher.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, dataWatcher);
|
||||||
|
|
||||||
|
return packetPlayOutNamedEntitySpawn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_12_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.server.v1_12_R1.EnumGamemode;
|
||||||
|
import net.minecraft.server.v1_12_R1.IChatBaseComponent;
|
||||||
|
import net.minecraft.server.v1_12_R1.PacketPlayOutPlayerInfo;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutPlayerInfoWrapper {
|
||||||
|
|
||||||
|
private final Class<?> packetPlayOutPlayerInfoClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo");
|
||||||
|
private final Class<?> playerInfoDataClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo$PlayerInfoData");
|
||||||
|
private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz,
|
||||||
|
packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class);
|
||||||
|
|
||||||
|
public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) {
|
||||||
|
PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo();
|
||||||
|
Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class)
|
||||||
|
.set(packetPlayOutPlayerInfo, action);
|
||||||
|
|
||||||
|
Object playerInfoData = playerInfoDataConstructor.invoke(packetPlayOutPlayerInfo,
|
||||||
|
gameProfile, 1, EnumGamemode.NOT_SET,
|
||||||
|
IChatBaseComponent.ChatSerializer.b("{\"text\":\"" + ChatColor.BLUE + "[NPC] " + name + "\"}")
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflection.FieldAccessor<List> fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(), "b", List.class);
|
||||||
|
List list = fieldAccessor.get(packetPlayOutPlayerInfo);
|
||||||
|
list.add(playerInfoData);
|
||||||
|
fieldAccessor.set(packetPlayOutPlayerInfo, list);
|
||||||
|
|
||||||
|
return packetPlayOutPlayerInfo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_12_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_12_R1.PacketPlayOutScoreboardTeam;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutScoreboardTeamWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createRegisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "f", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.FieldAccessor<Collection> collectionFieldAccessor = Reflection.getField(
|
||||||
|
packetPlayOutScoreboardTeam.getClass(), "h", Collection.class);
|
||||||
|
Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam);
|
||||||
|
collection.add(name);
|
||||||
|
collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms-v1_8_R1</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.8-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R1;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.nms.holograms.Hologram;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R1.packets.PacketPlayOutEntityHeadRotationWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R1.packets.PacketPlayOutNamedEntitySpawnWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R1.packets.PacketPlayOutPlayerInfoWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R1.packets.PacketPlayOutScoreboardTeamWrapper;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import net.minecraft.server.v1_8_R1.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_8_R1.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPC_v1_8_R1 extends NPC {
|
||||||
|
|
||||||
|
private Hologram hologram;
|
||||||
|
private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn;
|
||||||
|
private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister;
|
||||||
|
private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove;
|
||||||
|
private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation;
|
||||||
|
private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy;
|
||||||
|
|
||||||
|
public NPC_v1_8_R1(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
super(plugin, skin, autoHideDistance, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
this.hologram = new Hologram(location.clone().add(0, 0.5, 0), lines);
|
||||||
|
hologram.generatePackets(false);
|
||||||
|
|
||||||
|
this.gameProfile = generateGameProfile(uuid, name);
|
||||||
|
PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper();
|
||||||
|
|
||||||
|
// Packets for spawning the NPC:
|
||||||
|
this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createRegisterTeam(name); // First packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper()
|
||||||
|
.create(uuid, location, entityId); // Third packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper()
|
||||||
|
.create(location, entityId); // Fourth packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed).
|
||||||
|
|
||||||
|
// Packet for destroying the NPC:
|
||||||
|
this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send.
|
||||||
|
|
||||||
|
// Second packet to send is "packetPlayOutPlayerInfoRemove".
|
||||||
|
|
||||||
|
this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createUnregisterTeam(name); // Third packet to send.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendShowPackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoAdd);
|
||||||
|
playerConnection.sendPacket(packetPlayOutNamedEntitySpawn);
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityHeadRotation);
|
||||||
|
|
||||||
|
hologram.spawn(player);
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHidePackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityDestroy);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove);
|
||||||
|
|
||||||
|
hologram.destroy(player);
|
||||||
|
|
||||||
|
// Sending this a bit later so the player doesn't see the name (for that split second).
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R1.PacketPlayOutEntityHeadRotation;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutEntityHeadRotationWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutEntityHeadRotation create(Location location, int entityId) {
|
||||||
|
PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class).
|
||||||
|
set(packetPlayOutEntityHeadRotation, entityId);
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class)
|
||||||
|
.set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F));
|
||||||
|
|
||||||
|
return packetPlayOutEntityHeadRotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R1.DataWatcher;
|
||||||
|
import net.minecraft.server.v1_8_R1.PacketPlayOutNamedEntitySpawn;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutNamedEntitySpawnWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) {
|
||||||
|
PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, entityId);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, uuid);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getX() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getY() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getZ() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F)));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F)));
|
||||||
|
|
||||||
|
DataWatcher dataWatcher = new DataWatcher(null);
|
||||||
|
dataWatcher.a(10, (byte) 127);
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "i", DataWatcher.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, dataWatcher);
|
||||||
|
|
||||||
|
return packetPlayOutNamedEntitySpawn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.server.v1_8_R1.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutPlayerInfoWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutPlayerInfo create(EnumPlayerInfoAction action, GameProfile gameProfile, String name) {
|
||||||
|
PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo();
|
||||||
|
Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", EnumPlayerInfoAction.class)
|
||||||
|
.set(packetPlayOutPlayerInfo, action);
|
||||||
|
|
||||||
|
PlayerInfoData playerInfoData = new PlayerInfoData(packetPlayOutPlayerInfo, gameProfile,
|
||||||
|
1, EnumGamemode.NOT_SET, ChatSerializer.a(name));
|
||||||
|
|
||||||
|
Reflection.FieldAccessor<List> fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(),
|
||||||
|
"b", List.class);
|
||||||
|
|
||||||
|
List<PlayerInfoData> list = fieldAccessor.get(packetPlayOutPlayerInfo);
|
||||||
|
list.add(playerInfoData);
|
||||||
|
fieldAccessor.set(packetPlayOutPlayerInfo, list);
|
||||||
|
|
||||||
|
return packetPlayOutPlayerInfo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R1.PacketPlayOutScoreboardTeam;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutScoreboardTeamWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createRegisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "h", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
// Could not get this working in the PacketPlayOutPlayerInfoWrapper class.
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "c", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, ChatColor.BLUE + "[NPC] ");
|
||||||
|
Reflection.FieldAccessor<Collection> collectionFieldAccessor = Reflection.getField(
|
||||||
|
packetPlayOutScoreboardTeam.getClass(), "g", Collection.class);
|
||||||
|
Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam);
|
||||||
|
collection.add(name);
|
||||||
|
collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "h", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms-v1_8_R2</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.8.3-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R2;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.nms.holograms.Hologram;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R2.packets.PacketPlayOutEntityHeadRotationWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R2.packets.PacketPlayOutNamedEntitySpawnWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R2.packets.PacketPlayOutPlayerInfoWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R2.packets.PacketPlayOutScoreboardTeamWrapper;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import net.minecraft.server.v1_8_R2.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_8_R2.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPC_v1_8_R2 extends NPC {
|
||||||
|
|
||||||
|
private Hologram hologram;
|
||||||
|
private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn;
|
||||||
|
private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister;
|
||||||
|
private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove;
|
||||||
|
private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation;
|
||||||
|
private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy;
|
||||||
|
|
||||||
|
public NPC_v1_8_R2(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
super(plugin, skin, autoHideDistance, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
this.hologram = new Hologram(location.clone().add(0, 0.5, 0), lines);
|
||||||
|
hologram.generatePackets(false);
|
||||||
|
|
||||||
|
this.gameProfile = generateGameProfile(uuid, name);
|
||||||
|
PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper();
|
||||||
|
|
||||||
|
// Packets for spawning the NPC:
|
||||||
|
this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createRegisterTeam(name); // First packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper()
|
||||||
|
.create(uuid, location, entityId); // Third packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper()
|
||||||
|
.create(location, entityId); // Fourth packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed).
|
||||||
|
|
||||||
|
// Packet for destroying the NPC:
|
||||||
|
this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send.
|
||||||
|
|
||||||
|
// Second packet to send is "packetPlayOutPlayerInfoRemove".
|
||||||
|
|
||||||
|
this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createUnregisterTeam(name); // Third packet to send.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendShowPackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoAdd);
|
||||||
|
playerConnection.sendPacket(packetPlayOutNamedEntitySpawn);
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityHeadRotation);
|
||||||
|
|
||||||
|
hologram.spawn(player);
|
||||||
|
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHidePackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityDestroy);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove);
|
||||||
|
|
||||||
|
hologram.destroy(player);
|
||||||
|
|
||||||
|
// Sending this a bit later so the player doesn't see the name (for that split second).
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R2.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R2.PacketPlayOutEntityHeadRotation;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutEntityHeadRotationWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutEntityHeadRotation create(Location location, int entityId) {
|
||||||
|
PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class).
|
||||||
|
set(packetPlayOutEntityHeadRotation, entityId);
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class)
|
||||||
|
.set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F));
|
||||||
|
|
||||||
|
return packetPlayOutEntityHeadRotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R2.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R2.DataWatcher;
|
||||||
|
import net.minecraft.server.v1_8_R2.PacketPlayOutNamedEntitySpawn;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutNamedEntitySpawnWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) {
|
||||||
|
PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, entityId);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, uuid);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getX() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getY() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getZ() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F)));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F)));
|
||||||
|
|
||||||
|
DataWatcher dataWatcher = new DataWatcher(null);
|
||||||
|
dataWatcher.a(10, (byte) 127);
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "i", DataWatcher.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, dataWatcher);
|
||||||
|
|
||||||
|
return packetPlayOutNamedEntitySpawn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R2.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.server.v1_8_R2.IChatBaseComponent;
|
||||||
|
import net.minecraft.server.v1_8_R2.PacketPlayOutPlayerInfo;
|
||||||
|
import net.minecraft.server.v1_8_R2.WorldSettings;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutPlayerInfoWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) {
|
||||||
|
PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo();
|
||||||
|
Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class)
|
||||||
|
.set(packetPlayOutPlayerInfo, action);
|
||||||
|
|
||||||
|
PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData = packetPlayOutPlayerInfo.new PlayerInfoData(gameProfile, 1,
|
||||||
|
WorldSettings.EnumGamemode.NOT_SET, IChatBaseComponent.ChatSerializer.a(name));
|
||||||
|
|
||||||
|
Reflection.FieldAccessor<List> fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(),
|
||||||
|
"b", List.class);
|
||||||
|
|
||||||
|
List<PacketPlayOutPlayerInfo.PlayerInfoData> list = fieldAccessor.get(packetPlayOutPlayerInfo);
|
||||||
|
list.add(playerInfoData);
|
||||||
|
fieldAccessor.set(packetPlayOutPlayerInfo, list);
|
||||||
|
|
||||||
|
return packetPlayOutPlayerInfo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R2.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R2.PacketPlayOutScoreboardTeam;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutScoreboardTeamWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createRegisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "h", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
// Could not get this working in the PacketPlayOutPlayerInfoWrapper class.
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "c", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, ChatColor.BLUE + "[NPC] ");
|
||||||
|
Reflection.FieldAccessor<Collection> collectionFieldAccessor = Reflection.getField(
|
||||||
|
packetPlayOutScoreboardTeam.getClass(), "g", Collection.class);
|
||||||
|
Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam);
|
||||||
|
collection.add(name);
|
||||||
|
collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "h", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms-v1_8_R3</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.8.8-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R3;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.nms.holograms.Hologram;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R3.packets.PacketPlayOutEntityHeadRotationWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R3.packets.PacketPlayOutNamedEntitySpawnWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R3.packets.PacketPlayOutPlayerInfoWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_8_R3.packets.PacketPlayOutScoreboardTeamWrapper;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import net.minecraft.server.v1_8_R3.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPC_v1_8_R3 extends NPC {
|
||||||
|
|
||||||
|
private Hologram hologram;
|
||||||
|
private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn;
|
||||||
|
private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister;
|
||||||
|
private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove;
|
||||||
|
private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation;
|
||||||
|
private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy;
|
||||||
|
|
||||||
|
public NPC_v1_8_R3(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
super(plugin, skin, autoHideDistance, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
this.hologram = new Hologram(location.clone().add(0, 0.5, 0), lines);
|
||||||
|
hologram.generatePackets(false);
|
||||||
|
|
||||||
|
this.gameProfile = generateGameProfile(uuid, name);
|
||||||
|
PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper();
|
||||||
|
|
||||||
|
// Packets for spawning the NPC:
|
||||||
|
this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createRegisterTeam(name); // First packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper()
|
||||||
|
.create(uuid, location, entityId); // Third packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper()
|
||||||
|
.create(location, entityId); // Fourth packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed).
|
||||||
|
|
||||||
|
// Packet for destroying the NPC:
|
||||||
|
this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send.
|
||||||
|
|
||||||
|
// Second packet to send is "packetPlayOutPlayerInfoRemove".
|
||||||
|
|
||||||
|
this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createUnregisterTeam(name); // Third packet to send.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendShowPackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoAdd);
|
||||||
|
playerConnection.sendPacket(packetPlayOutNamedEntitySpawn);
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityHeadRotation);
|
||||||
|
|
||||||
|
hologram.spawn(player);
|
||||||
|
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHidePackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityDestroy);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove);
|
||||||
|
|
||||||
|
hologram.destroy(player);
|
||||||
|
|
||||||
|
// Sending this a bit later so the player doesn't see the name (for that split second).
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R3.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R3.PacketPlayOutEntityHeadRotation;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutEntityHeadRotationWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutEntityHeadRotation create(Location location, int entityId) {
|
||||||
|
PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class).
|
||||||
|
set(packetPlayOutEntityHeadRotation, entityId);
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class)
|
||||||
|
.set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F));
|
||||||
|
|
||||||
|
return packetPlayOutEntityHeadRotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R3.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R3.DataWatcher;
|
||||||
|
import net.minecraft.server.v1_8_R3.PacketPlayOutNamedEntitySpawn;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutNamedEntitySpawnWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) {
|
||||||
|
PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, entityId);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, uuid);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getX() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getY() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getZ() * 32.0D));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F)));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F)));
|
||||||
|
|
||||||
|
DataWatcher dataWatcher = new DataWatcher(null);
|
||||||
|
dataWatcher.a(10, (byte) 127);
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "i", DataWatcher.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, dataWatcher);
|
||||||
|
|
||||||
|
return packetPlayOutNamedEntitySpawn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R3.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.server.v1_8_R3.IChatBaseComponent;
|
||||||
|
import net.minecraft.server.v1_8_R3.PacketPlayOutPlayerInfo;
|
||||||
|
import net.minecraft.server.v1_8_R3.WorldSettings;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutPlayerInfoWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) {
|
||||||
|
PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo();
|
||||||
|
Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class)
|
||||||
|
.set(packetPlayOutPlayerInfo, action);
|
||||||
|
|
||||||
|
PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData = packetPlayOutPlayerInfo.new PlayerInfoData(gameProfile, 1,
|
||||||
|
WorldSettings.EnumGamemode.NOT_SET, IChatBaseComponent.ChatSerializer.a(name));
|
||||||
|
|
||||||
|
Reflection.FieldAccessor<List> fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(),
|
||||||
|
"b", List.class);
|
||||||
|
|
||||||
|
List<PacketPlayOutPlayerInfo.PlayerInfoData> list = fieldAccessor.get(packetPlayOutPlayerInfo);
|
||||||
|
list.add(playerInfoData);
|
||||||
|
fieldAccessor.set(packetPlayOutPlayerInfo, list);
|
||||||
|
|
||||||
|
return packetPlayOutPlayerInfo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_8_R3.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_8_R3.PacketPlayOutScoreboardTeam;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutScoreboardTeamWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createRegisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "h", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
// Could not get this working in the PacketPlayOutPlayerInfoWrapper class.
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "c", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, ChatColor.BLUE + "[NPC] ");
|
||||||
|
Reflection.FieldAccessor<Collection> collectionFieldAccessor = Reflection.getField(
|
||||||
|
packetPlayOutScoreboardTeam.getClass(), "g", Collection.class);
|
||||||
|
Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam);
|
||||||
|
collection.add(name);
|
||||||
|
collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "h", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms-v1_9_R1</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.9-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R1;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.nms.holograms.Hologram;
|
||||||
|
import net.jitse.npclib.nms.v1_9_R1.packets.PacketPlayOutEntityHeadRotationWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_9_R1.packets.PacketPlayOutNamedEntitySpawnWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_9_R1.packets.PacketPlayOutPlayerInfoWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_9_R1.packets.PacketPlayOutScoreboardTeamWrapper;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import net.minecraft.server.v1_9_R1.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_9_R1.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPC_v1_9_R1 extends NPC {
|
||||||
|
|
||||||
|
private Hologram hologram;
|
||||||
|
private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn;
|
||||||
|
private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister;
|
||||||
|
private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove;
|
||||||
|
private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation;
|
||||||
|
private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy;
|
||||||
|
|
||||||
|
public NPC_v1_9_R1(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
super(plugin, skin, autoHideDistance, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
this.hologram = new Hologram(location.clone().subtract(0, 0.5, 0), lines);
|
||||||
|
hologram.generatePackets(false);
|
||||||
|
|
||||||
|
this.gameProfile = generateGameProfile(uuid, name);
|
||||||
|
PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper();
|
||||||
|
|
||||||
|
// Packets for spawning the NPC:
|
||||||
|
this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createRegisterTeam(name); // First packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper()
|
||||||
|
.create(uuid, location, entityId); // Third packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper()
|
||||||
|
.create(location, entityId); // Fourth packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed).
|
||||||
|
|
||||||
|
// Packet for destroying the NPC:
|
||||||
|
this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send.
|
||||||
|
|
||||||
|
// Second packet to send is "packetPlayOutPlayerInfoRemove".
|
||||||
|
|
||||||
|
this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createUnregisterTeam(name); // Third packet to send.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendShowPackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoAdd);
|
||||||
|
playerConnection.sendPacket(packetPlayOutNamedEntitySpawn);
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityHeadRotation);
|
||||||
|
|
||||||
|
hologram.spawn(player);
|
||||||
|
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHidePackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityDestroy);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove);
|
||||||
|
|
||||||
|
hologram.destroy(player);
|
||||||
|
|
||||||
|
// Sending this a bit later so the player doesn't see the name (for that split second).
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_9_R1.PacketPlayOutEntityHeadRotation;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutEntityHeadRotationWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutEntityHeadRotation create(Location location, int entityId) {
|
||||||
|
PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class).
|
||||||
|
set(packetPlayOutEntityHeadRotation, entityId);
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class)
|
||||||
|
.set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F));
|
||||||
|
|
||||||
|
return packetPlayOutEntityHeadRotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_9_R1.DataWatcher;
|
||||||
|
import net.minecraft.server.v1_9_R1.DataWatcherObject;
|
||||||
|
import net.minecraft.server.v1_9_R1.DataWatcherRegistry;
|
||||||
|
import net.minecraft.server.v1_9_R1.PacketPlayOutNamedEntitySpawn;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutNamedEntitySpawnWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) {
|
||||||
|
PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, entityId);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, uuid);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getX());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getY());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getZ());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F)));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F)));
|
||||||
|
|
||||||
|
DataWatcher dataWatcher = new DataWatcher(null);
|
||||||
|
dataWatcher.register(new DataWatcherObject<>(12, DataWatcherRegistry.a), (byte) 127);
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "h", DataWatcher.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, dataWatcher);
|
||||||
|
|
||||||
|
return packetPlayOutNamedEntitySpawn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.server.v1_9_R1.IChatBaseComponent;
|
||||||
|
import net.minecraft.server.v1_9_R1.PacketPlayOutPlayerInfo;
|
||||||
|
import net.minecraft.server.v1_9_R1.WorldSettings;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutPlayerInfoWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) {
|
||||||
|
PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo();
|
||||||
|
Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class)
|
||||||
|
.set(packetPlayOutPlayerInfo, action);
|
||||||
|
|
||||||
|
PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData = new PacketPlayOutPlayerInfo().new PlayerInfoData(gameProfile, 1,
|
||||||
|
WorldSettings.EnumGamemode.NOT_SET, IChatBaseComponent.ChatSerializer.b("{\"text\":\"" + ChatColor.BLUE + "[NPC] " + name + "\"}"));
|
||||||
|
|
||||||
|
Reflection.FieldAccessor<List> fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(),
|
||||||
|
"b", List.class);
|
||||||
|
|
||||||
|
List<PacketPlayOutPlayerInfo.PlayerInfoData> list = fieldAccessor.get(packetPlayOutPlayerInfo);
|
||||||
|
list.add(playerInfoData);
|
||||||
|
fieldAccessor.set(packetPlayOutPlayerInfo, list);
|
||||||
|
|
||||||
|
return packetPlayOutPlayerInfo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R1.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_9_R1.PacketPlayOutScoreboardTeam;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutScoreboardTeamWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createRegisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "f", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.FieldAccessor<Collection> collectionFieldAccessor = Reflection.getField(
|
||||||
|
packetPlayOutScoreboardTeam.getClass(), "h", Collection.class);
|
||||||
|
Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam);
|
||||||
|
collection.add(name);
|
||||||
|
collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib-nms</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-nms-v1_9_R2</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.9.4-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R2;
|
||||||
|
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.nms.holograms.Hologram;
|
||||||
|
import net.jitse.npclib.nms.v1_9_R2.packets.PacketPlayOutEntityHeadRotationWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_9_R2.packets.PacketPlayOutNamedEntitySpawnWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_9_R2.packets.PacketPlayOutPlayerInfoWrapper;
|
||||||
|
import net.jitse.npclib.nms.v1_9_R2.packets.PacketPlayOutScoreboardTeamWrapper;
|
||||||
|
import net.jitse.npclib.skin.Skin;
|
||||||
|
import net.minecraft.server.v1_9_R2.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_9_R2.entity.CraftPlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPC_v1_9_R2 extends NPC {
|
||||||
|
|
||||||
|
private Hologram hologram;
|
||||||
|
private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn;
|
||||||
|
private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister;
|
||||||
|
private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove;
|
||||||
|
private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation;
|
||||||
|
private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy;
|
||||||
|
|
||||||
|
public NPC_v1_9_R2(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
|
||||||
|
super(plugin, skin, autoHideDistance, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
this.hologram = new Hologram(location.clone().subtract(0, 0.5, 0), lines);
|
||||||
|
hologram.generatePackets(false);
|
||||||
|
|
||||||
|
this.gameProfile = generateGameProfile(uuid, name);
|
||||||
|
PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper();
|
||||||
|
|
||||||
|
// Packets for spawning the NPC:
|
||||||
|
this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createRegisterTeam(name); // First packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper()
|
||||||
|
.create(uuid, location, entityId); // Third packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper()
|
||||||
|
.create(location, entityId); // Fourth packet to send.
|
||||||
|
|
||||||
|
this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper
|
||||||
|
.create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed).
|
||||||
|
|
||||||
|
// Packet for destroying the NPC:
|
||||||
|
this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send.
|
||||||
|
|
||||||
|
// Second packet to send is "packetPlayOutPlayerInfoRemove".
|
||||||
|
|
||||||
|
this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper()
|
||||||
|
.createUnregisterTeam(name); // Third packet to send.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendShowPackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoAdd);
|
||||||
|
playerConnection.sendPacket(packetPlayOutNamedEntitySpawn);
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityHeadRotation);
|
||||||
|
|
||||||
|
hologram.spawn(player);
|
||||||
|
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHidePackets(Player player) {
|
||||||
|
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection;
|
||||||
|
|
||||||
|
playerConnection.sendPacket(packetPlayOutEntityDestroy);
|
||||||
|
playerConnection.sendPacket(packetPlayOutPlayerInfoRemove);
|
||||||
|
|
||||||
|
hologram.destroy(player);
|
||||||
|
|
||||||
|
// Sending this a bit later so the player doesn't see the name (for that split second).
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () ->
|
||||||
|
playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R2.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_9_R2.PacketPlayOutEntityHeadRotation;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutEntityHeadRotationWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutEntityHeadRotation create(Location location, int entityId) {
|
||||||
|
PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class).
|
||||||
|
set(packetPlayOutEntityHeadRotation, entityId);
|
||||||
|
Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class)
|
||||||
|
.set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F));
|
||||||
|
|
||||||
|
return packetPlayOutEntityHeadRotation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R2.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_9_R2.DataWatcher;
|
||||||
|
import net.minecraft.server.v1_9_R2.DataWatcherObject;
|
||||||
|
import net.minecraft.server.v1_9_R2.DataWatcherRegistry;
|
||||||
|
import net.minecraft.server.v1_9_R2.PacketPlayOutNamedEntitySpawn;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutNamedEntitySpawnWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) {
|
||||||
|
PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, entityId);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, uuid);
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getX());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getY());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", double.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, location.getZ());
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F)));
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F)));
|
||||||
|
|
||||||
|
DataWatcher dataWatcher = new DataWatcher(null);
|
||||||
|
dataWatcher.register(new DataWatcherObject<>(12, DataWatcherRegistry.a), (byte) 127);
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "h", DataWatcher.class)
|
||||||
|
.set(packetPlayOutNamedEntitySpawn, dataWatcher);
|
||||||
|
|
||||||
|
return packetPlayOutNamedEntitySpawn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R2.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.server.v1_9_R2.IChatBaseComponent;
|
||||||
|
import net.minecraft.server.v1_9_R2.PacketPlayOutPlayerInfo;
|
||||||
|
import net.minecraft.server.v1_9_R2.WorldSettings;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutPlayerInfoWrapper {
|
||||||
|
|
||||||
|
private final Class<?> packetPlayOutPlayerInfoClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo");
|
||||||
|
private final Class<?> playerInfoDataClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo$PlayerInfoData");
|
||||||
|
private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz,
|
||||||
|
packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, WorldSettings.EnumGamemode.class, IChatBaseComponent.class);
|
||||||
|
|
||||||
|
public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) {
|
||||||
|
PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo();
|
||||||
|
Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class)
|
||||||
|
.set(packetPlayOutPlayerInfo, action);
|
||||||
|
|
||||||
|
Object playerInfoData = playerInfoDataConstructor.invoke(packetPlayOutPlayerInfo,
|
||||||
|
gameProfile, 1, WorldSettings.EnumGamemode.NOT_SET,
|
||||||
|
IChatBaseComponent.ChatSerializer.b("{\"text\":\"" + ChatColor.BLUE + "[NPC] " + name + "\"}")
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflection.FieldAccessor<List> fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(), "b", List.class);
|
||||||
|
List list = fieldAccessor.get(packetPlayOutPlayerInfo);
|
||||||
|
list.add(playerInfoData);
|
||||||
|
fieldAccessor.set(packetPlayOutPlayerInfo, list);
|
||||||
|
|
||||||
|
return packetPlayOutPlayerInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.nms.v1_9_R2.packets;
|
||||||
|
|
||||||
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
|
import net.minecraft.server.v1_9_R2.PacketPlayOutScoreboardTeam;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutScoreboardTeamWrapper {
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createRegisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "f", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, "never");
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 0);
|
||||||
|
Reflection.FieldAccessor<Collection> collectionFieldAccessor = Reflection.getField(
|
||||||
|
packetPlayOutScoreboardTeam.getClass(), "h", Collection.class);
|
||||||
|
Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam);
|
||||||
|
collection.add(name);
|
||||||
|
collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) {
|
||||||
|
PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam();
|
||||||
|
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, 1);
|
||||||
|
Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class)
|
||||||
|
.set(packetPlayOutScoreboardTeam, name);
|
||||||
|
|
||||||
|
return packetPlayOutScoreboardTeam;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>npclib-plugin</artifactId>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>NPCLib</finalName>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>npclib-api</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.plugin;
|
||||||
|
|
||||||
|
import net.jitse.npclib.NPCLib;
|
||||||
|
import net.jitse.npclib.api.NPC;
|
||||||
|
import net.jitse.npclib.plugin.listeners.NPCListener;
|
||||||
|
import net.jitse.npclib.skin.MineSkinFetcher;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPCLibPlugin extends JavaPlugin implements Listener {
|
||||||
|
|
||||||
|
private NPCLib npcLib;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
this.npcLib = new NPCLib(this);
|
||||||
|
getServer().getConsoleSender().sendMessage(ChatColor.BLUE + "[NPCLib] " + ChatColor.WHITE + "plugin enabled.");
|
||||||
|
getServer().getConsoleSender().sendMessage(ChatColor.BLUE + "[NPCLib] " +
|
||||||
|
ChatColor.GRAY + "This is a test plugin usually used for development reasons. " +
|
||||||
|
"You can spawn NPCs by pressing [shift] in game.");
|
||||||
|
|
||||||
|
|
||||||
|
getServer().getPluginManager().registerEvents(this, this);
|
||||||
|
getServer().getPluginManager().registerEvents(new NPCListener(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
getServer().getConsoleSender().sendMessage(ChatColor.BLUE + "[NPCLib] " + ChatColor.WHITE + "plugin disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerShift(PlayerToggleSneakEvent event) {
|
||||||
|
if (event.isSneaking()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MineSkinFetcher.fetchSkinFromIdAsync(168841, skin -> {
|
||||||
|
NPC npc = npcLib.createNPC(skin, Arrays.asList(
|
||||||
|
ChatColor.BOLD + "NPC Library", "",
|
||||||
|
"Create your own", "non-player characters",
|
||||||
|
"with the simplistic", "API of NPCLib!"
|
||||||
|
));
|
||||||
|
npc.create(event.getPlayer().getLocation());
|
||||||
|
|
||||||
|
for (Player player : getServer().getOnlinePlayers()) {
|
||||||
|
npc.show(player);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jitse Boonstra
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.jitse.npclib.plugin.listeners;
|
||||||
|
|
||||||
|
import net.jitse.npclib.events.NPCDestroyEvent;
|
||||||
|
import net.jitse.npclib.events.NPCInteractEvent;
|
||||||
|
import net.jitse.npclib.events.NPCSpawnEvent;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jitse Boonstra
|
||||||
|
*/
|
||||||
|
public class NPCListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onNPCSpawn(NPCSpawnEvent event) {
|
||||||
|
event.getPlayer().sendMessage(ChatColor.GREEN + "Spawned NPC " + event.getNPC().getEntityId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onNPCDestroy(NPCDestroyEvent event) {
|
||||||
|
event.getPlayer().sendMessage(ChatColor.RED + "Destroyed NPC " + event.getNPC().getEntityId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onNPCInteract(NPCInteractEvent event) {
|
||||||
|
event.getWhoClicked().sendMessage(ChatColor.BLUE + "Interacted with NPC "
|
||||||
|
+ event.getNPC().getEntityId() + " type " + event.getClickType());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
name: NPCLib
|
||||||
|
version: 1.0.4
|
||||||
|
author: JitseB
|
||||||
|
main: net.jitse.npclib.plugin.NPCLibPlugin
|
||||||
|
description: An NPC library.
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<groupId>net.jitse</groupId>
|
||||||
|
<artifactId>npclib</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
|
||||||
|
<name>NPCLib</name>
|
||||||
|
<url>https://github.com/JitseB/npclib</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>MIT</name>
|
||||||
|
<url>https://opensource.org/licenses/MIT</url>
|
||||||
|
<distribution>repo</distribution>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<defaultGoal>clean install</defaultGoal>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.6.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spigot-repo</id>
|
||||||
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!--Spigot API-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot-api</artifactId>
|
||||||
|
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!--Spigot API and NMS-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>commons</module>
|
||||||
|
<module>nms</module>
|
||||||
|
<module>api</module>
|
||||||
|
<module>plugin</module>
|
||||||
|
</modules>
|
||||||
|
</project>
|
Loading…
Reference in New Issue