Skip to content

Commit

Permalink
Add nms from other project
Browse files Browse the repository at this point in the history
  • Loading branch information
dig committed Nov 16, 2020
1 parent a6881ef commit bfbe90b
Show file tree
Hide file tree
Showing 15 changed files with 519 additions and 17 deletions.
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf

32 changes: 15 additions & 17 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
plugins {
id 'com.github.johnrengelman.shadow' version '6.0.0'
id 'maven'
id 'java'
}
subprojects {
apply plugin: 'java'

group 'com.github.chain-plugins'
version '1.0'
group 'com.github.chain-plugins'
version '1.0-SNAPSHOT'

repositories {
mavenCentral()
mavenLocal()
maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
maven { url = 'https://oss.sonatype.org/content/repositories/snapshots' }
}
repositories {
mavenCentral()
mavenLocal()
maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
maven { url = 'https://oss.sonatype.org/content/repositories/snapshots' }
}

dependencies {
annotationProcessor 'org.projectlombok:lombok:1.18.8'
compileOnly 'org.projectlombok:lombok:1.18.8'
compileOnly 'org.spigotmc:spigot-api:1.16.1-R0.1-SNAPSHOT'
dependencies {
annotationProcessor 'org.projectlombok:lombok:1.18.8'
compileOnly 'org.projectlombok:lombok:1.18.8'
compileOnly 'org.spigotmc:spigot-api:1.16.1-R0.1-SNAPSHOT'
}
}
13 changes: 13 additions & 0 deletions bukkit/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
repositories {
maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
maven { url = 'https://repo.codemc.org/repository/maven-public/' }
}

dependencies {
compile project(':nms')
compile project(':v1_8_R3')
compile project(':v1_11_R1')
compile project(':v1_14_R1')
compileOnly 'org.spigotmc:spigot-api:1.16.4-R0.1-SNAPSHOT'
compileOnly 'org.bukkit:craftbukkit:1.16.4-R0.1-SNAPSHOT'
}
110 changes: 110 additions & 0 deletions bukkit/src/main/java/com/github/chain/inventory/Serializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.github.chain.inventory;

import com.github.chain.inventory.nms.InvalidMinecraftVersionException;
import com.github.chain.inventory.nms.InventoryNMS;
import com.github.chain.inventory.nms.MinecraftVersion;
import com.github.chain.inventory.nms.v1_11_R1.v1_11_R1NMS;
import com.github.chain.inventory.nms.v1_14_R1.v1_14_R1NMS;
import com.github.chain.inventory.nms.v1_8_R3.v1_8_R3NMS;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;

import java.lang.reflect.Array;
import java.lang.reflect.Method;

public class Serializer {

private static InventoryNMS nmsBridge;
private static MinecraftVersion version;

public static String encode(@NonNull Inventory inventory) throws Exception {
checkIfProviderIsSet();
ItemStack[] inventoryContents = inventory.getContents();
Class<?> nmsItemStackClazz = getItemStackClass();

Object nmsItemArray = Array.newInstance(nmsItemStackClazz, inventoryContents.length);
for (int i = 0; i < inventoryContents.length; i++) {
Array.set(nmsItemArray, i, toNMSItem(inventoryContents[i]));
}

return nmsBridge.encode((Object[]) nmsItemArray);
}

public static void decode(@NonNull String encoded, @NonNull Inventory inventory) throws Exception {
checkIfProviderIsSet();
Object[] nmsItemStacks = nmsBridge.decode(encoded);

ItemStack[] inventoryContents = new ItemStack[nmsItemStacks.length];
for (int i = 0; i < nmsItemStacks.length; i++) {
inventoryContents[i] = toBukkitItem(nmsItemStacks[i]);
}

inventory.setContents(inventoryContents);
}

private static ItemStack toBukkitItem(Object itemStack) throws Exception {
Class<?> nmsItemStackClazz = getItemStackClass();
Method asBukkitCopy = getCraftItemStackClass().getMethod("asBukkitCopy", nmsItemStackClazz);
return (ItemStack) asBukkitCopy.invoke(null, itemStack);
}

private static Object toNMSItem(ItemStack itemStack) throws Exception {
Method asNMSCopy = getCraftItemStackClass().getMethod("asNMSCopy", ItemStack.class);
return asNMSCopy.invoke(null, itemStack);
}

private static Class<?> getCraftItemStackClass() throws ClassNotFoundException {
return Class.forName("org.bukkit.craftbukkit." + version.toString() + ".inventory.CraftItemStack");
}

private static Class<?> getItemStackClass() throws ClassNotFoundException {
return Class.forName("net.minecraft.server." + version.toString() + ".ItemStack");
}

private static void checkIfProviderIsSet() throws InvalidMinecraftVersionException {
if (nmsBridge == null) {
String versionName = Bukkit.getServer().getClass().getPackage().getName();

MinecraftVersion mcVersion;
try {
mcVersion = MinecraftVersion.valueOf(versionName.substring(versionName.lastIndexOf('.') + 1));
} catch (IllegalArgumentException e) {
throw new InvalidMinecraftVersionException("Version of Minecraft not supported.");
}

InventoryNMS bridge;
switch (mcVersion) {
case v1_8_R3:
case v1_9_R1:
case v1_9_R2:
case v1_10_R1:
bridge = new v1_8_R3NMS();
break;
case v1_11_R1:
case v1_12_R1:
case v1_13_R1:
case v1_13_R2:
bridge = new v1_11_R1NMS();
break;
case v1_14_R1:
case v1_15_R1:
case v1_16_R1:
case v1_16_R2:
case v1_16_R3:
bridge = new v1_14_R1NMS();
break;
default:
throw new InvalidMinecraftVersionException("Version of Minecraft not supported.");
}

version = mcVersion;
nmsBridge = bridge;

if (!nmsBridge.init(mcVersion)) {
throw new InvalidMinecraftVersionException("Version of Minecraft not supported.");
}
}
}
}
Empty file added nms/build.gradle
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.chain.inventory.nms;

public class InvalidMinecraftVersionException extends Exception {
public InvalidMinecraftVersionException(String errorMessage) {
super(errorMessage);
}
}
12 changes: 12 additions & 0 deletions nms/src/main/java/com/github/chain/inventory/nms/InventoryNMS.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.chain.inventory.nms;

import java.lang.reflect.InvocationTargetException;

public interface InventoryNMS {

boolean init(MinecraftVersion version);

String encode(Object[] craftItemStacks) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException;

Object[] decode(String encoded) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.chain.inventory.nms;

public enum MinecraftVersion {

v1_8_R3,
v1_9_R1,
v1_9_R2,
v1_10_R1,
v1_11_R1,
v1_12_R1,
v1_13_R1,
v1_13_R2,
v1_14_R1,
v1_15_R1,
v1_16_R1,
v1_16_R2,
v1_16_R3

}
6 changes: 6 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
rootProject.name = 'bukkit-inventory-serializer'

include 'bukkit'

include 'nms'
include 'v1_8_R3'
include 'v1_11_R1'
include 'v1_14_R1'
3 changes: 3 additions & 0 deletions v1_11_R1/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {
compileOnly project(':nms')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.github.chain.inventory.nms.v1_11_R1;

import com.github.chain.inventory.nms.MinecraftVersion;
import com.github.chain.inventory.nms.InventoryNMS;
import lombok.extern.java.Log;

import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.logging.Level;

@Log
public class v1_11_R1NMS implements InventoryNMS {

private Class<?> nbtTagListClass;
private Class<?> nbtItemStackClass;
private Class<?> nbtBaseClass;
private Class<?> nbtTagCompoundClass;
private Class<?> nbtReadLimiterClass;

private Method writeNbt;
private Method readNbt;

@Override
public boolean init(MinecraftVersion version) {
Class<?> nbtToolsClass;
try {
nbtToolsClass = Class.forName("net.minecraft.server." + version.toString() + ".NBTCompressedStreamTools");

nbtReadLimiterClass = Class.forName("net.minecraft.server." + version.toString() + ".NBTReadLimiter");
nbtTagListClass = Class.forName("net.minecraft.server." + version.toString() + ".NBTTagList");
nbtItemStackClass = Class.forName("net.minecraft.server." + version.toString() + ".ItemStack");
nbtBaseClass = Class.forName("net.minecraft.server." + version.toString() + ".NBTBase");
nbtTagCompoundClass = Class.forName("net.minecraft.server." + version.toString() + ".NBTTagCompound");
} catch (ClassNotFoundException e) {
log.log(Level.SEVERE, "Unable to find classes needed for NBT. Are you sure we support this Minecraft version?", e);
return false;
}

try {
writeNbt = nbtToolsClass.getDeclaredMethod("a", nbtBaseClass, DataOutput.class);
writeNbt.setAccessible(true);
readNbt = nbtToolsClass.getDeclaredMethod("a", DataInput.class, Integer.TYPE, nbtReadLimiterClass);
readNbt.setAccessible(true);
} catch (NoSuchMethodException e) {
log.log(Level.SEVERE, "Unable to find writeNbt or readNbt method. Are you sure we support this Minecraft version?", e);
return false;
}

return true;
}

@Override
public String encode(Object[] craftItemStacks) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);

Object nbtTagList = nbtTagListClass.newInstance();
Method nbtTagListAddMethod = nbtTagListClass.getMethod("add", nbtBaseClass);
Method itemStackSaveMethod = nbtItemStackClass.getMethod("save", nbtTagCompoundClass);

for (int i = 0; i < craftItemStacks.length; ++i) {
Object nbtTagCompound = nbtTagCompoundClass.newInstance();
Object itemStack = nbtItemStackClass.cast(craftItemStacks[i]);
if (itemStack != null) {
itemStackSaveMethod.invoke(itemStack, nbtTagCompound);
}
nbtTagListAddMethod.invoke(nbtTagList, nbtTagCompound);
}

writeNbt.invoke(null, nbtTagList, dataOutputStream);
return new String(Base64.getEncoder().encode(byteArrayOutputStream.toByteArray()));
}

@Override
public Object[] decode(String encoded) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(encoded));
DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

Object nbtReadLimiter = nbtReadLimiterClass.getConstructor(long.class).newInstance(Long.MAX_VALUE);
Object readInvoke = readNbt.invoke(null, dataInputStream, 0, nbtReadLimiter);

Object nbtTagList = nbtTagListClass.cast(readInvoke);
Method nbtTagListSizeMethod = nbtTagListClass.getMethod("size");
Method nbtTagListGetMethod = nbtTagListClass.getMethod("get", int.class);
int nbtTagListSize = (int) nbtTagListSizeMethod.invoke(nbtTagList);

Method nbtTagCompoundIsEmptyMethod = nbtTagCompoundClass.getMethod("isEmpty");
Object items = Array.newInstance(nbtItemStackClass, nbtTagListSize);

Constructor<?> nbtItemStackConstructor = nbtItemStackClass.getDeclaredConstructor(nbtTagCompoundClass);
nbtItemStackConstructor.setAccessible(true);

for (int i = 0; i < nbtTagListSize; ++i) {
Object nbtTagCompound = nbtTagListGetMethod.invoke(nbtTagList, i);
boolean isEmpty = (boolean) nbtTagCompoundIsEmptyMethod.invoke(nbtTagCompound);
if (!isEmpty) {
Array.set(items, i, nbtItemStackConstructor.newInstance(nbtTagCompound));
}
}

return (Object[]) items;
}
}

3 changes: 3 additions & 0 deletions v1_14_R1/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {
compileOnly project(':nms')
}
Loading

0 comments on commit bfbe90b

Please sign in to comment.