diff --git a/.github/workflows/buildtools.sh b/.github/workflows/buildtools.sh
index 13cad4c..c5da7e3 100644
--- a/.github/workflows/buildtools.sh
+++ b/.github/workflows/buildtools.sh
@@ -35,4 +35,5 @@ checkVersion "1.20.6" "21"
checkVersion "1.21" "21"
checkVersion "1.21.3" "21"
checkVersion "1.21.4" "21"
-checkVersion "1.21.5" "21"
\ No newline at end of file
+checkVersion "1.21.5" "21"
+checkVersion "1.21.6" "21"
\ No newline at end of file
diff --git a/zip-api/src/main/java/net/imprex/zip/api/ZIPHandler.java b/zip-api/src/main/java/net/imprex/zip/api/ZIPHandler.java
index f29e5fd..be4c32f 100644
--- a/zip-api/src/main/java/net/imprex/zip/api/ZIPHandler.java
+++ b/zip-api/src/main/java/net/imprex/zip/api/ZIPHandler.java
@@ -16,4 +16,6 @@ public interface ZIPHandler {
ZIPBackpackType getBackpackType(ItemStack item);
boolean isBackpack(ItemStack item);
+
+ ZIPUniqueId getUniqueId(ItemStack item);
}
diff --git a/zip-common/src/main/java/net/imprex/zip/common/BPConstants.java b/zip-common/src/main/java/net/imprex/zip/common/BPConstants.java
new file mode 100644
index 0000000..a7f1cc1
--- /dev/null
+++ b/zip-common/src/main/java/net/imprex/zip/common/BPConstants.java
@@ -0,0 +1,19 @@
+package net.imprex.zip.common;
+
+public class BPConstants {
+
+ public static final int VERSION = 2;
+ public static final int INVENTORY_VERSION = 2;
+
+ public static final String KEY_VERSION = "version";
+
+ public static final String KEY_ID = "id";
+ public static final String KEY_TYPE_RAW = "typeRaw";
+ public static final String KEY_INVENTORY = "inventory";
+
+ public static final String KEY_INVENTORY_VERSION = "version";
+ public static final String KEY_INVENTORY_DATA_VERSION = "dataVersion";
+ public static final String KEY_INVENTORY_ITEMS = "items";
+ public static final String KEY_INVENTORY_ITEMS_SIZE = "itemsSize";
+ public static final String KEY_INVENTORY_SLOT = "ZIPslot";
+}
diff --git a/zip-common/src/main/java/net/imprex/zip/common/MinecraftVersion.java b/zip-common/src/main/java/net/imprex/zip/common/MinecraftVersion.java
index 3e3cfb9..408c70f 100644
--- a/zip-common/src/main/java/net/imprex/zip/common/MinecraftVersion.java
+++ b/zip-common/src/main/java/net/imprex/zip/common/MinecraftVersion.java
@@ -1,24 +1,24 @@
+/**
+ * @author Imprex-Development
+ * @see MinecraftVersion.java
+ */
package net.imprex.zip.common;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Bukkit;
-/**
- * @author Imprex-Development
- * @see https://github.com/Imprex-Development/orebfuscator/blob/master/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/MinecraftVersion.java
- */
-public final class MinecraftVersion implements Comparable {
+public final class MinecraftVersion {
private static final class NmsMapping {
private static final List MAPPINGS = new ArrayList<>();
static {
+ MAPPINGS.add(new NmsMapping("1.21.6", "v1_21_R5"));
MAPPINGS.add(new NmsMapping("1.21.5", "v1_21_R4"));
MAPPINGS.add(new NmsMapping("1.21.4", "v1_21_R3"));
MAPPINGS.add(new NmsMapping("1.21.3", "v1_21_R2"));
@@ -26,11 +26,11 @@ private static final class NmsMapping {
MAPPINGS.add(new NmsMapping("1.20.5", "v1_20_R4"));
}
- public static String get(MinecraftVersion version) {
+ public static String get(Version version) {
for (NmsMapping mapping : MAPPINGS) {
if (version.isAtOrAbove(mapping.version)) {
if (mapping.version.minor() != version.minor()) {
- System.out.println(String.format("Using nms mapping with mismatched minor versions: %s - %s",
+ ZIPLogger.warn(String.format("Using nms mapping with mismatched minor versions: %s - %s",
mapping.version, version));
}
@@ -41,19 +41,17 @@ public static String get(MinecraftVersion version) {
throw new RuntimeException("Can't get nms package version for minecraft version: " + version);
}
- private final MinecraftVersion version;
+ private final Version version;
private final String nmsVersion;
public NmsMapping(String version, String nmsVersion) {
- this.version = new MinecraftVersion(version);
+ this.version = Version.parse(version);
this.nmsVersion = nmsVersion;
}
}
- private static final Pattern VERSION_PATTERN = Pattern.compile("(?\\d+)(?:\\.(?\\d+))(?:\\.(?\\d+))?");
private static final Pattern PACKAGE_PATTERN = Pattern.compile("org\\.bukkit\\.craftbukkit\\.(v\\d+_\\d+_R\\d+)");
-
- private static final MinecraftVersion CURRENT_VERSION = new MinecraftVersion(Bukkit.getBukkitVersion());
+ private static final Version CURRENT_VERSION = Version.parse(Bukkit.getBukkitVersion());
private static String NMS_VERSION;
@@ -72,118 +70,35 @@ public static String nmsVersion() {
return NMS_VERSION;
}
+ public static Version current() {
+ return CURRENT_VERSION;
+ }
+
public static int majorVersion() {
- return CURRENT_VERSION.major;
+ return CURRENT_VERSION.major();
}
public static int minorVersion() {
- return CURRENT_VERSION.minor;
+ return CURRENT_VERSION.minor();
}
public static int patchVersion() {
- return CURRENT_VERSION.patch;
+ return CURRENT_VERSION.patch();
}
public static boolean isAbove(String version) {
- return CURRENT_VERSION.isAbove(new MinecraftVersion(version));
+ return CURRENT_VERSION.isAbove(Version.parse(version));
}
public static boolean isAtOrAbove(String version) {
- return CURRENT_VERSION.isAtOrAbove(new MinecraftVersion(version));
+ return CURRENT_VERSION.isAtOrAbove(Version.parse(version));
}
public static boolean isAtOrBelow(String version) {
- return CURRENT_VERSION.isAtOrBelow(new MinecraftVersion(version));
+ return CURRENT_VERSION.isAtOrBelow(Version.parse(version));
}
public static boolean isBelow(String version) {
- return CURRENT_VERSION.isBelow(new MinecraftVersion(version));
- }
-
- private final int major;
- private final int minor;
- private final int patch;
-
- public MinecraftVersion(String version) {
- Matcher matcher = VERSION_PATTERN.matcher(version);
-
- if (!matcher.find()) {
- throw new IllegalArgumentException("can't parse minecraft version: " + version);
- }
-
- this.major = Integer.parseInt(matcher.group("major"));
- this.minor = Integer.parseInt(matcher.group("minor"));
-
- String patch = matcher.group("patch");
- if (patch != null) {
- this.patch = Integer.parseInt(patch);
- } else {
- this.patch = 0;
- }
- }
-
- public int major() {
- return this.major;
- }
-
- public int minor() {
- return this.minor;
- }
-
- public int patch() {
- return this.patch;
- }
-
- public boolean isAbove(MinecraftVersion version) {
- return this.compareTo(version) > 0;
- }
-
- public boolean isAtOrAbove(MinecraftVersion version) {
- return this.compareTo(version) >= 0;
- }
-
- public boolean isAtOrBelow(MinecraftVersion version) {
- return this.compareTo(version) <= 0;
- }
-
- public boolean isBelow(MinecraftVersion version) {
- return this.compareTo(version) < 0;
- }
-
- @Override
- public int compareTo(MinecraftVersion other) {
- int major = Integer.compare(this.major, other.major);
- if (major != 0) {
- return major;
- }
-
- int minor = Integer.compare(this.minor, other.minor);
- if (minor != 0) {
- return minor;
- }
-
- return Integer.compare(this.patch, other.patch);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(major, minor, patch);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof MinecraftVersion)) {
- return false;
- }
- MinecraftVersion other = (MinecraftVersion) obj;
- return major == other.major && minor == other.minor && patch == other.patch;
- }
-
- @Override
- public String toString() {
- return String.format("%s.%s.%s", this.major, this.minor, this.patch);
+ return CURRENT_VERSION.isBelow(Version.parse(version));
}
}
\ No newline at end of file
diff --git a/zip-common/src/main/java/net/imprex/zip/common/Version.java b/zip-common/src/main/java/net/imprex/zip/common/Version.java
new file mode 100644
index 0000000..729f905
--- /dev/null
+++ b/zip-common/src/main/java/net/imprex/zip/common/Version.java
@@ -0,0 +1,107 @@
+/**
+ * @author Imprex-Development
+ * @see Version.java
+ */
+package net.imprex.zip.common;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+public record Version(int major, int minor, int patch) implements Comparable {
+
+ private static final Pattern VERSION_PATTERN = Pattern.compile("(?\\d+)(?:\\.(?\\d+))?(?:\\.(?\\d+))?");
+
+ public static Version parse(String version) {
+ Matcher matcher = VERSION_PATTERN.matcher(version);
+
+ if (!matcher.find()) {
+ throw new IllegalArgumentException("can't parse version: " + version);
+ }
+
+ int major = Integer.parseInt(matcher.group("major"));
+
+ String minorGroup = matcher.group("minor");
+ int minor = minorGroup != null
+ ? Integer.parseInt(minorGroup)
+ : 0;
+
+ String patchGroup = matcher.group("patch");
+ int patch = patchGroup != null
+ ? Integer.parseInt(patchGroup)
+ : 0;
+
+ return new Version(major, minor, patch);
+ }
+
+ public boolean isAbove(Version version) {
+ return this.compareTo(version) > 0;
+ }
+
+ public boolean isAtOrAbove(Version version) {
+ return this.compareTo(version) >= 0;
+ }
+
+ public boolean isAtOrBelow(Version version) {
+ return this.compareTo(version) <= 0;
+ }
+
+ public boolean isBelow(Version version) {
+ return this.compareTo(version) < 0;
+ }
+
+ @Override
+ public int compareTo(Version other) {
+ int major = Integer.compare(this.major, other.major);
+ if (major != 0) {
+ return major;
+ }
+
+ int minor = Integer.compare(this.minor, other.minor);
+ if (minor != 0) {
+ return minor;
+ }
+
+ return Integer.compare(this.patch, other.patch);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(major, minor, patch);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Version)) {
+ return false;
+ }
+ Version other = (Version) obj;
+ return major == other.major && minor == other.minor && patch == other.patch;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s.%s.%s", this.major, this.minor, this.patch);
+ }
+
+ public static final class Json extends TypeAdapter {
+
+ @Override
+ public void write(JsonWriter out, Version value) throws IOException {
+ out.value(value.toString());
+ }
+
+ @Override
+ public Version read(JsonReader in) throws IOException {
+ return Version.parse(in.nextString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/zip-plugin/src/main/java/net/imprex/zip/util/ZIPLogger.java b/zip-common/src/main/java/net/imprex/zip/common/ZIPLogger.java
similarity index 77%
rename from zip-plugin/src/main/java/net/imprex/zip/util/ZIPLogger.java
rename to zip-common/src/main/java/net/imprex/zip/common/ZIPLogger.java
index 83fd3a5..9d8a693 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/util/ZIPLogger.java
+++ b/zip-common/src/main/java/net/imprex/zip/common/ZIPLogger.java
@@ -1,4 +1,4 @@
-package net.imprex.zip.util;
+package net.imprex.zip.common;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -16,6 +16,10 @@ public static void setVerbose(boolean verbose) {
ZIPLogger.verbose = verbose;
}
+ public static void log(Level level, String message) {
+ ZIPLogger.logger.log(level, LOG_PREFIX + message);
+ }
+
public static void debug(String message) {
if (ZIPLogger.verbose) {
ZIPLogger.logger.log(Level.FINE, LOG_DEBUG_PREFIX + message);
@@ -30,6 +34,10 @@ public static void warn(String message) {
ZIPLogger.logger.log(Level.WARNING, LOG_PREFIX + message);
}
+ public static void warn(String message, Throwable throwable) {
+ ZIPLogger.logger.log(Level.WARNING, LOG_PREFIX + message, throwable);
+ }
+
public static void error(String message, Throwable throwable) {
ZIPLogger.logger.log(Level.SEVERE, LOG_PREFIX + message, throwable);
}
diff --git a/zip-nms/pom.xml b/zip-nms/pom.xml
index 2409ce8..a064cdc 100644
--- a/zip-nms/pom.xml
+++ b/zip-nms/pom.xml
@@ -23,5 +23,6 @@
zip-nms-v1_21_R2
zip-nms-v1_21_R3
zip-nms-v1_21_R4
+ zip-nms-v1_21_R5
\ No newline at end of file
diff --git a/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/ItemStackContainerResult.java b/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/ItemStackContainerResult.java
new file mode 100644
index 0000000..3e5390d
--- /dev/null
+++ b/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/ItemStackContainerResult.java
@@ -0,0 +1,6 @@
+package net.imprex.zip.nms.api;
+
+import java.util.List;
+
+public record ItemStackContainerResult(int containerSize, List items) {
+}
diff --git a/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/ItemStackWithSlot.java b/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/ItemStackWithSlot.java
new file mode 100644
index 0000000..d1bdc29
--- /dev/null
+++ b/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/ItemStackWithSlot.java
@@ -0,0 +1,6 @@
+package net.imprex.zip.nms.api;
+
+import org.bukkit.inventory.ItemStack;
+
+public record ItemStackWithSlot(int slot, ItemStack item) {
+}
diff --git a/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/NmsManager.java b/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/NmsManager.java
index 7bcaa4e..01c789d 100644
--- a/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/NmsManager.java
+++ b/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/NmsManager.java
@@ -1,16 +1,18 @@
package net.imprex.zip.nms.api;
-import java.util.List;
-
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonObject;
+
public interface NmsManager {
- byte[] itemstackToBinary(ItemStack[] items);
+ JsonObject itemstackToJsonElement(ItemStack[] items);
- List binaryToItemStack(byte[] binary);
+ ItemStackContainerResult jsonElementToItemStack(JsonObject jsonElement);
+
+ JsonObject migrateToJsonElement(byte[] binary);
void setSkullProfile(SkullMeta meta, String texture);
diff --git a/zip-nms/zip-nms-v1_19_R1/src/main/java/net/imprex/zip/nms/v1_19_R1/ZipNmsManager.java b/zip-nms/zip-nms-v1_19_R1/src/main/java/net/imprex/zip/nms/v1_19_R1/ZipNmsManager.java
index 2e1ebd5..36fae81 100644
--- a/zip-nms/zip-nms-v1_19_R1/src/main/java/net/imprex/zip/nms/v1_19_R1/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_19_R1/src/main/java/net/imprex/zip/nms/v1_19_R1/ZipNmsManager.java
@@ -1,7 +1,7 @@
package net.imprex.zip.nms.v1_19_R1;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -12,15 +12,32 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
+import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.resources.RegistryOps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
public class ZipNmsManager implements NmsManager {
@@ -28,50 +45,113 @@ public class ZipNmsManager implements NmsManager {
private static final Method CRAFTMETASKULL_SET_PROFILE = ReflectionUtil.getMethod(CRAFTMETASKULL_CLASS,
"setProfile", GameProfile.class);
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream);
- } catch (Exception e) {
- e.printStackTrace();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+
+ private static final DynamicOps DYNAMIC_OPS_NBT = RegistryOps.create(NbtOps.INSTANCE, DEFAULT_REGISTRY);
+ private static final DynamicOps DYNAMIC_OPS_JSON = RegistryOps.create(JsonOps.INSTANCE, DEFAULT_REGISTRY);
+
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- list.add(craftItem.save(new CompoundTag()));
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
+ }
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow(false, error -> {});
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag) {
- items.add(CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of((CompoundTag) base)));
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow(false, error -> {});
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_19_R2/src/main/java/net/imprex/zip/nms/v1_19_R2/ZipNmsManager.java b/zip-nms/zip-nms-v1_19_R2/src/main/java/net/imprex/zip/nms/v1_19_R2/ZipNmsManager.java
index 4b80fb1..3410dfc 100644
--- a/zip-nms/zip-nms-v1_19_R2/src/main/java/net/imprex/zip/nms/v1_19_R2/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_19_R2/src/main/java/net/imprex/zip/nms/v1_19_R2/ZipNmsManager.java
@@ -1,7 +1,7 @@
package net.imprex.zip.nms.v1_19_R2;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -12,15 +12,32 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
+import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.resources.RegistryOps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
public class ZipNmsManager implements NmsManager {
@@ -28,50 +45,113 @@ public class ZipNmsManager implements NmsManager {
private static final Method CRAFTMETASKULL_SET_PROFILE = ReflectionUtil.getMethod(CRAFTMETASKULL_CLASS,
"setProfile", GameProfile.class);
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream);
- } catch (Exception e) {
- e.printStackTrace();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+
+ private static final DynamicOps DYNAMIC_OPS_NBT = RegistryOps.create(NbtOps.INSTANCE, DEFAULT_REGISTRY);
+ private static final DynamicOps DYNAMIC_OPS_JSON = RegistryOps.create(JsonOps.INSTANCE, DEFAULT_REGISTRY);
+
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- list.add(craftItem.save(new CompoundTag()));
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
+ }
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow(false, error -> {});
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag) {
- items.add(CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of((CompoundTag) base)));
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow(false, error -> {});
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_19_R3/src/main/java/net/imprex/zip/nms/v1_19_R3/ZipNmsManager.java b/zip-nms/zip-nms-v1_19_R3/src/main/java/net/imprex/zip/nms/v1_19_R3/ZipNmsManager.java
index 33b2dd1..2afcf22 100644
--- a/zip-nms/zip-nms-v1_19_R3/src/main/java/net/imprex/zip/nms/v1_19_R3/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_19_R3/src/main/java/net/imprex/zip/nms/v1_19_R3/ZipNmsManager.java
@@ -1,7 +1,7 @@
package net.imprex.zip.nms.v1_19_R3;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -12,15 +12,32 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
+import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.resources.RegistryOps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
public class ZipNmsManager implements NmsManager {
@@ -28,50 +45,113 @@ public class ZipNmsManager implements NmsManager {
private static final Method CRAFTMETASKULL_SET_PROFILE = ReflectionUtil.getMethod(CRAFTMETASKULL_CLASS,
"setProfile", GameProfile.class);
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream);
- } catch (Exception e) {
- e.printStackTrace();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+
+ private static final DynamicOps DYNAMIC_OPS_NBT = RegistryOps.create(NbtOps.INSTANCE, DEFAULT_REGISTRY);
+ private static final DynamicOps DYNAMIC_OPS_JSON = RegistryOps.create(JsonOps.INSTANCE, DEFAULT_REGISTRY);
+
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- list.add(craftItem.save(new CompoundTag()));
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
+ }
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow(false, error -> {});
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag) {
- items.add(CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of((CompoundTag) base)));
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow(false, error -> {});
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_20_R1/src/main/java/net/imprex/zip/nms/v1_20_R1/ZipNmsManager.java b/zip-nms/zip-nms-v1_20_R1/src/main/java/net/imprex/zip/nms/v1_20_R1/ZipNmsManager.java
index ecc1f92..5b72e6a 100644
--- a/zip-nms/zip-nms-v1_20_R1/src/main/java/net/imprex/zip/nms/v1_20_R1/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_20_R1/src/main/java/net/imprex/zip/nms/v1_20_R1/ZipNmsManager.java
@@ -1,7 +1,7 @@
package net.imprex.zip.nms.v1_20_R1;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -12,15 +12,32 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
+import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.resources.RegistryOps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
public class ZipNmsManager implements NmsManager {
@@ -28,50 +45,113 @@ public class ZipNmsManager implements NmsManager {
private static final Method CRAFTMETASKULL_SET_PROFILE = ReflectionUtil.getMethod(CRAFTMETASKULL_CLASS,
"setProfile", GameProfile.class);
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream);
- } catch (Exception e) {
- e.printStackTrace();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+
+ private static final DynamicOps DYNAMIC_OPS_NBT = RegistryOps.create(NbtOps.INSTANCE, DEFAULT_REGISTRY);
+ private static final DynamicOps DYNAMIC_OPS_JSON = RegistryOps.create(JsonOps.INSTANCE, DEFAULT_REGISTRY);
+
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- list.add(craftItem.save(new CompoundTag()));
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
+ }
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow(false, error -> {});
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag) {
- items.add(CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of((CompoundTag) base)));
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow(false, error -> {});
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_20_R2/src/main/java/net/imprex/zip/nms/v1_20_R2/ZipNmsManager.java b/zip-nms/zip-nms-v1_20_R2/src/main/java/net/imprex/zip/nms/v1_20_R2/ZipNmsManager.java
index aa53f74..4b9eb75 100644
--- a/zip-nms/zip-nms-v1_20_R2/src/main/java/net/imprex/zip/nms/v1_20_R2/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_20_R2/src/main/java/net/imprex/zip/nms/v1_20_R2/ZipNmsManager.java
@@ -1,7 +1,7 @@
package net.imprex.zip.nms.v1_20_R2;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -12,15 +12,32 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
+import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.resources.RegistryOps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
public class ZipNmsManager implements NmsManager {
@@ -28,50 +45,113 @@ public class ZipNmsManager implements NmsManager {
private static final Method CRAFTMETASKULL_SET_PROFILE = ReflectionUtil.getMethod(CRAFTMETASKULL_CLASS,
"setProfile", GameProfile.class);
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream);
- } catch (Exception e) {
- e.printStackTrace();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+
+ private static final DynamicOps DYNAMIC_OPS_NBT = RegistryOps.create(NbtOps.INSTANCE, DEFAULT_REGISTRY);
+ private static final DynamicOps DYNAMIC_OPS_JSON = RegistryOps.create(JsonOps.INSTANCE, DEFAULT_REGISTRY);
+
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- list.add(craftItem.save(new CompoundTag()));
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
+ }
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow(false, error -> {});
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag) {
- items.add(CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of((CompoundTag) base)));
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow(false, error -> {});
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_20_R3/src/main/java/net/imprex/zip/nms/v1_20_R3/ZipNmsManager.java b/zip-nms/zip-nms-v1_20_R3/src/main/java/net/imprex/zip/nms/v1_20_R3/ZipNmsManager.java
index 3cab8ee..ac7c775 100644
--- a/zip-nms/zip-nms-v1_20_R3/src/main/java/net/imprex/zip/nms/v1_20_R3/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_20_R3/src/main/java/net/imprex/zip/nms/v1_20_R3/ZipNmsManager.java
@@ -1,7 +1,7 @@
package net.imprex.zip.nms.v1_20_R3;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -12,16 +12,33 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
+import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.resources.RegistryOps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
public class ZipNmsManager implements NmsManager {
@@ -29,50 +46,113 @@ public class ZipNmsManager implements NmsManager {
private static final Method CRAFTMETASKULL_SET_PROFILE = ReflectionUtil.getMethod(CRAFTMETASKULL_CLASS,
"setProfile", GameProfile.class);
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
- } catch (Exception e) {
- e.printStackTrace();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+
+ private static final DynamicOps DYNAMIC_OPS_NBT = RegistryOps.create(NbtOps.INSTANCE, DEFAULT_REGISTRY);
+ private static final DynamicOps DYNAMIC_OPS_JSON = RegistryOps.create(JsonOps.INSTANCE, DEFAULT_REGISTRY);
+
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- list.add(craftItem.save(new CompoundTag()));
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
+ }
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow(false, error -> {});
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag) {
- items.add(CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of((CompoundTag) base)));
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow(false, error -> {});
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow(false, error -> {}).getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_20_R4/src/main/java/net/imprex/zip/nms/v1_20_R4/ZipNmsManager.java b/zip-nms/zip-nms-v1_20_R4/src/main/java/net/imprex/zip/nms/v1_20_R4/ZipNmsManager.java
index 73b2f24..b168ca8 100644
--- a/zip-nms/zip-nms-v1_20_R4/src/main/java/net/imprex/zip/nms/v1_20_R4/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_20_R4/src/main/java/net/imprex/zip/nms/v1_20_R4/ZipNmsManager.java
@@ -1,30 +1,43 @@
package net.imprex.zip.nms.v1_20_R4;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import org.bukkit.Material;
-import org.bukkit.craftbukkit.v1_20_R4.CraftRegistry;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
public class ZipNmsManager implements NmsManager {
@@ -32,70 +45,113 @@ public class ZipNmsManager implements NmsManager {
private static final Method CRAFTMETASKULL_SET_PROFILE = ReflectionUtil.getMethod(CRAFTMETASKULL_CLASS,
"setProfile", GameProfile.class);
- private static final RegistryAccess DEFAULT_REGISTRY = CraftRegistry.getMinecraftRegistry();
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- private static final CompoundTag NBT_EMPTY_ITEMSTACK = new CompoundTag();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
- static {
- NBT_EMPTY_ITEMSTACK.putString("id", "minecraft:air");
- }
+ private static final DynamicOps DYNAMIC_OPS_NBT = DEFAULT_REGISTRY.createSerializationContext(NbtOps.INSTANCE);
+ private static final DynamicOps DYNAMIC_OPS_JSON = DEFAULT_REGISTRY.createSerializationContext(JsonOps.INSTANCE);
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return null;
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
- } catch (Exception e) {
- e.printStackTrace();
+ @Override
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
}
- return new CompoundTag();
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow();
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
+ }
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- if (itemStack == null || itemStack.getType() == Material.AIR) {
- list.add(NBT_EMPTY_ITEMSTACK);
- } else {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- Tag tag = craftItem.save(DEFAULT_REGISTRY);
- list.add(tag);
- }
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
- }
+
+ ListTag list = compound.getList("i", 10);
- @Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag itemTag) {
- if (itemTag.getString("id").equals("minecraft:air")) {
- items.add(new ItemStack(Material.AIR));
- } else {
- Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, itemTag);
- if (optional.isPresent()) {
- items.add(CraftItemStack.asBukkitCopy(optional.get()));
- }
- }
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow();
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_21_R1/src/main/java/net/imprex/zip/nms/v1_21_R1/ZipNmsManager.java b/zip-nms/zip-nms-v1_21_R1/src/main/java/net/imprex/zip/nms/v1_21_R1/ZipNmsManager.java
index 36248a6..dac5638 100644
--- a/zip-nms/zip-nms-v1_21_R1/src/main/java/net/imprex/zip/nms/v1_21_R1/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_21_R1/src/main/java/net/imprex/zip/nms/v1_21_R1/ZipNmsManager.java
@@ -1,45 +1,60 @@
package net.imprex.zip.nms.v1_21_R1;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import java.util.function.BiConsumer;
import org.bukkit.Material;
-import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
import net.minecraft.world.item.component.ResolvableProfile;
public class ZipNmsManager implements NmsManager {
- private static final BiConsumer SET_PROFILE;
-
- private static final RegistryAccess DEFAULT_REGISTRY = CraftRegistry.getMinecraftRegistry();
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- private static final CompoundTag NBT_EMPTY_ITEMSTACK = new CompoundTag();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+ private static final DynamicOps DYNAMIC_OPS_NBT = DEFAULT_REGISTRY.createSerializationContext(NbtOps.INSTANCE);
+ private static final DynamicOps DYNAMIC_OPS_JSON = DEFAULT_REGISTRY.createSerializationContext(JsonOps.INSTANCE);
+
+ private static final BiConsumer SET_PROFILE;
+
static {
- NBT_EMPTY_ITEMSTACK.putString("id", "minecraft:air");
-
BiConsumer setProfile = (meta, profile) -> {
throw new NullPointerException("Unable to find 'setProfile' method!");
};
@@ -73,62 +88,105 @@ public class ZipNmsManager implements NmsManager {
SET_PROFILE = setProfile;
}
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
- } catch (Exception e) {
- e.printStackTrace();
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- if (itemStack == null || itemStack.getType() == Material.AIR) {
- list.add(NBT_EMPTY_ITEMSTACK);
- } else {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- Tag tag = craftItem.save(DEFAULT_REGISTRY);
- list.add(tag);
- }
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow();
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
+ }
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag itemTag) {
- if (itemTag.getString("id").equals("minecraft:air")) {
- items.add(new ItemStack(Material.AIR));
- } else {
- Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, itemTag);
- if (optional.isPresent()) {
- items.add(CraftItemStack.asBukkitCopy(optional.get()));
- }
- }
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow();
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_21_R2/src/main/java/net/imprex/zip/nms/v1_21_R2/ZipNmsManager.java b/zip-nms/zip-nms-v1_21_R2/src/main/java/net/imprex/zip/nms/v1_21_R2/ZipNmsManager.java
index bd63f72..391ce46 100644
--- a/zip-nms/zip-nms-v1_21_R2/src/main/java/net/imprex/zip/nms/v1_21_R2/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_21_R2/src/main/java/net/imprex/zip/nms/v1_21_R2/ZipNmsManager.java
@@ -1,45 +1,60 @@
package net.imprex.zip.nms.v1_21_R2;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import java.util.function.BiConsumer;
import org.bukkit.Material;
-import org.bukkit.craftbukkit.v1_21_R2.CraftRegistry;
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
import net.minecraft.world.item.component.ResolvableProfile;
public class ZipNmsManager implements NmsManager {
- private static final BiConsumer SET_PROFILE;
-
- private static final RegistryAccess DEFAULT_REGISTRY = CraftRegistry.getMinecraftRegistry();
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- private static final CompoundTag NBT_EMPTY_ITEMSTACK = new CompoundTag();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+ private static final DynamicOps DYNAMIC_OPS_NBT = DEFAULT_REGISTRY.createSerializationContext(NbtOps.INSTANCE);
+ private static final DynamicOps DYNAMIC_OPS_JSON = DEFAULT_REGISTRY.createSerializationContext(JsonOps.INSTANCE);
+
+ private static final BiConsumer SET_PROFILE;
+
static {
- NBT_EMPTY_ITEMSTACK.putString("id", "minecraft:air");
-
BiConsumer setProfile = (meta, profile) -> {
throw new NullPointerException("Unable to find 'setProfile' method!");
};
@@ -73,62 +88,105 @@ public class ZipNmsManager implements NmsManager {
SET_PROFILE = setProfile;
}
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
- } catch (Exception e) {
- e.printStackTrace();
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- if (itemStack == null || itemStack.getType() == Material.AIR) {
- list.add(NBT_EMPTY_ITEMSTACK);
- } else {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- Tag tag = craftItem.save(DEFAULT_REGISTRY);
- list.add(tag);
- }
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow();
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
+ }
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag itemTag) {
- if (itemTag.getString("id").equals("minecraft:air")) {
- items.add(new ItemStack(Material.AIR));
- } else {
- Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, itemTag);
- if (optional.isPresent()) {
- items.add(CraftItemStack.asBukkitCopy(optional.get()));
- }
- }
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow();
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_21_R3/pom.xml b/zip-nms/zip-nms-v1_21_R3/pom.xml
index 89e6386..61e819d 100644
--- a/zip-nms/zip-nms-v1_21_R3/pom.xml
+++ b/zip-nms/zip-nms-v1_21_R3/pom.xml
@@ -1,7 +1,5 @@
-
- 4.0.0
+
+ 4.0.0
net.imprex
diff --git a/zip-nms/zip-nms-v1_21_R3/src/main/java/net/imprex/zip/nms/v1_21_R3/ZipNmsManager.java b/zip-nms/zip-nms-v1_21_R3/src/main/java/net/imprex/zip/nms/v1_21_R3/ZipNmsManager.java
index 7c2a710..bc8d73e 100644
--- a/zip-nms/zip-nms-v1_21_R3/src/main/java/net/imprex/zip/nms/v1_21_R3/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_21_R3/src/main/java/net/imprex/zip/nms/v1_21_R3/ZipNmsManager.java
@@ -1,45 +1,60 @@
package net.imprex.zip.nms.v1_21_R3;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import java.util.function.BiConsumer;
import org.bukkit.Material;
-import org.bukkit.craftbukkit.v1_21_R3.CraftRegistry;
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
import net.minecraft.world.item.component.ResolvableProfile;
public class ZipNmsManager implements NmsManager {
- private static final BiConsumer SET_PROFILE;
-
- private static final RegistryAccess DEFAULT_REGISTRY = CraftRegistry.getMinecraftRegistry();
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- private static final CompoundTag NBT_EMPTY_ITEMSTACK = new CompoundTag();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+ private static final DynamicOps DYNAMIC_OPS_NBT = DEFAULT_REGISTRY.createSerializationContext(NbtOps.INSTANCE);
+ private static final DynamicOps DYNAMIC_OPS_JSON = DEFAULT_REGISTRY.createSerializationContext(JsonOps.INSTANCE);
+
+ private static final BiConsumer SET_PROFILE;
+
static {
- NBT_EMPTY_ITEMSTACK.putString("id", "minecraft:air");
-
BiConsumer setProfile = (meta, profile) -> {
throw new NullPointerException("Unable to find 'setProfile' method!");
};
@@ -73,62 +88,105 @@ public class ZipNmsManager implements NmsManager {
SET_PROFILE = setProfile;
}
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
- } catch (Exception e) {
- e.printStackTrace();
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- return new CompoundTag();
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- if (itemStack == null || itemStack.getType() == Material.AIR) {
- list.add(NBT_EMPTY_ITEMSTACK);
- } else {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- Tag tag = craftItem.save(DEFAULT_REGISTRY);
- list.add(tag);
- }
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow();
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
+ }
+
+ return new ItemStackContainerResult(itemsSize, items);
}
-
+
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i", 9)) {
- ListTag list = nbt.getList("i", 10);
- for (Tag base : list) {
- if (base instanceof CompoundTag itemTag) {
- if (itemTag.getString("id").equals("minecraft:air")) {
- items.add(new ItemStack(Material.AIR));
- } else {
- Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, itemTag);
- if (optional.isPresent()) {
- items.add(CraftItemStack.asBukkitCopy(optional.get()));
- }
- }
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getList("i", 10);
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow();
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_21_R4/src/main/java/net/imprex/zip/nms/v1_21_R4/ZipNmsManager.java b/zip-nms/zip-nms-v1_21_R4/src/main/java/net/imprex/zip/nms/v1_21_R4/ZipNmsManager.java
index 4c51c9e..063907b 100644
--- a/zip-nms/zip-nms-v1_21_R4/src/main/java/net/imprex/zip/nms/v1_21_R4/ZipNmsManager.java
+++ b/zip-nms/zip-nms-v1_21_R4/src/main/java/net/imprex/zip/nms/v1_21_R4/ZipNmsManager.java
@@ -1,45 +1,60 @@
package net.imprex.zip.nms.v1_21_R4;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import java.util.function.BiConsumer;
import org.bukkit.Material;
-import org.bukkit.craftbukkit.v1_21_R4.CraftRegistry;
import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
import net.minecraft.world.item.component.ResolvableProfile;
public class ZipNmsManager implements NmsManager {
- private static final BiConsumer SET_PROFILE;
-
- private static final RegistryAccess DEFAULT_REGISTRY = CraftRegistry.getMinecraftRegistry();
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
- private static final CompoundTag NBT_EMPTY_ITEMSTACK = new CompoundTag();
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+ private static final DynamicOps DYNAMIC_OPS_NBT = DEFAULT_REGISTRY.createSerializationContext(NbtOps.INSTANCE);
+ private static final DynamicOps DYNAMIC_OPS_JSON = DEFAULT_REGISTRY.createSerializationContext(JsonOps.INSTANCE);
+
+ private static final BiConsumer SET_PROFILE;
+
static {
- NBT_EMPTY_ITEMSTACK.putString("id", "minecraft:air");
-
BiConsumer setProfile = (meta, profile) -> {
throw new NullPointerException("Unable to find 'setProfile' method!");
};
@@ -73,67 +88,105 @@ public class ZipNmsManager implements NmsManager {
SET_PROFILE = setProfile;
}
- public byte[] nbtToBinary(CompoundTag compound) {
- try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
- NbtIo.writeCompressed(compound, outputStream);
- return outputStream.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- public CompoundTag binaryToNBT(byte[] binary) {
- try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
- return NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
- } catch (Exception e) {
- e.printStackTrace();
- }
- return new CompoundTag();
- }
-
@Override
- public byte[] itemstackToBinary(ItemStack[] items) {
- CompoundTag inventory = new CompoundTag();
- ListTag list = new ListTag();
- for (ItemStack itemStack : items) {
- if (itemStack == null || itemStack.getType() == Material.AIR) {
- list.add(NBT_EMPTY_ITEMSTACK);
- } else {
- net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(itemStack);
- Tag tag = craftItem.save(DEFAULT_REGISTRY);
- list.add(tag);
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
}
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
}
- inventory.put("i", list);
- return nbtToBinary(inventory);
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
}
@Override
- public List binaryToItemStack(byte[] binary) {
- CompoundTag nbt = binaryToNBT(binary);
- List items = new ArrayList<>();
- if (nbt.contains("i")) {
- Optional list = nbt.getList("i");
- if (list.isEmpty()) {
- return items;
- }
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
+ }
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
- for (Tag base : list.get()) {
- if (base instanceof CompoundTag itemTag) {
- String itemType = itemTag.getString("id").orElse("");
- if (itemType.equals("minecraft:air")) {
- items.add(new ItemStack(Material.AIR));
- } else {
- Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, itemTag);
- if (optional.isPresent()) {
- items.add(CraftItemStack.asBukkitCopy(optional.get()));
- }
- }
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow();
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
+ }
+
+ return new ItemStackContainerResult(itemsSize, items);
+ }
+
+ @Override
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getListOrEmpty("i");
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id").orElse("");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
}
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow();
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
}
}
- return items;
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
}
@Override
diff --git a/zip-nms/zip-nms-v1_21_R5/pom.xml b/zip-nms/zip-nms-v1_21_R5/pom.xml
new file mode 100644
index 0000000..2e6e44e
--- /dev/null
+++ b/zip-nms/zip-nms-v1_21_R5/pom.xml
@@ -0,0 +1,70 @@
+
+ 4.0.0
+
+
+ net.imprex
+ zip-nms
+ ${revision}
+
+
+ zip-nms-v1_21_R5
+
+
+
+ net.imprex
+ zip-nms-api
+ ${revision}
+ provided
+
+
+ org.spigotmc
+ spigot
+ 1.21.6-R0.1-SNAPSHOT
+ remapped-mojang
+ provided
+
+
+
+
+
+
+ net.md-5
+ specialsource-maven-plugin
+ ${plugin.specialsource.version}
+
+
+ package
+
+ remap
+
+ remap-obf
+
+
+ org.spigotmc:minecraft-server:1.21.6-R0.1-SNAPSHOT:txt:maps-mojang
+ true
+
+ org.spigotmc:spigot:1.21.6-R0.1-SNAPSHOT:jar:remapped-mojang
+ true
+ remapped-obf
+
+
+
+ package
+
+ remap
+
+ remap-spigot
+
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
+
+ org.spigotmc:minecraft-server:1.21.6-R0.1-SNAPSHOT:csrg:maps-spigot
+
+ org.spigotmc:spigot:1.21.6-R0.1-SNAPSHOT:jar:remapped-obf
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/zip-nms/zip-nms-v1_21_R5/src/main/java/net/imprex/zip/nms/v1_21_R5/ZipNmsManager.java b/zip-nms/zip-nms-v1_21_R5/src/main/java/net/imprex/zip/nms/v1_21_R5/ZipNmsManager.java
new file mode 100644
index 0000000..41a9020
--- /dev/null
+++ b/zip-nms/zip-nms-v1_21_R5/src/main/java/net/imprex/zip/nms/v1_21_R5/ZipNmsManager.java
@@ -0,0 +1,208 @@
+package net.imprex.zip.nms.v1_21_R5;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.BiConsumer;
+
+import org.bukkit.Material;
+import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.SkullMeta;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.properties.Property;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.Dynamic;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.JsonOps;
+
+import net.imprex.zip.common.BPConstants;
+import net.imprex.zip.common.ReflectionUtil;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
+import net.imprex.zip.nms.api.NmsManager;
+import net.minecraft.SharedConstants;
+import net.minecraft.core.RegistryAccess;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.NbtAccounter;
+import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtOps;
+import net.minecraft.nbt.Tag;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
+import net.minecraft.world.item.component.ResolvableProfile;
+
+public class ZipNmsManager implements NmsManager {
+
+ private static final int DATA_VERSION = SharedConstants.getCurrentVersion().dataVersion().version();
+
+ @SuppressWarnings("deprecation")
+ private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
+
+ private static final DynamicOps DYNAMIC_OPS_NBT = DEFAULT_REGISTRY.createSerializationContext(NbtOps.INSTANCE);
+ private static final DynamicOps DYNAMIC_OPS_JSON = DEFAULT_REGISTRY.createSerializationContext(JsonOps.INSTANCE);
+
+ private static final BiConsumer SET_PROFILE;
+
+ static {
+ BiConsumer setProfile = (meta, profile) -> {
+ throw new NullPointerException("Unable to find 'setProfile' method!");
+ };
+
+ Class> craftMetaSkullClass = new ItemStack(Material.PLAYER_HEAD)
+ .getItemMeta()
+ .getClass();
+
+ Method setResolvableProfileMethod = ReflectionUtil.searchMethod(craftMetaSkullClass, void.class, ResolvableProfile.class);
+ if (setResolvableProfileMethod != null) {
+ setProfile = (meta, profile) -> {
+ try {
+ setResolvableProfileMethod.invoke(meta, new ResolvableProfile(profile));
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ };
+ } else {
+ Method setProfileMethod = ReflectionUtil.searchMethod(craftMetaSkullClass, void.class, GameProfile.class);
+ if (setProfileMethod != null) {
+ setProfile = (meta, profile) -> {
+ try {
+ setProfileMethod.invoke(meta, profile);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ };
+ }
+ }
+
+ SET_PROFILE = setProfile;
+ }
+
+ @Override
+ public JsonObject itemstackToJsonElement(ItemStack[] items) {
+ JsonArray jsonItems = new JsonArray();
+ for (int slot = 0; slot < items.length; slot++) {
+ ItemStack item = items[slot];
+ if (item == null || item.getType() == Material.AIR) {
+ continue;
+ }
+ net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, slot);
+ jsonItems.add(resultJson);
+ }
+
+ JsonObject outputJson = new JsonObject();
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ outputJson.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, items.length);
+ outputJson.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return outputJson;
+ }
+
+ @Override
+ public ItemStackContainerResult jsonElementToItemStack(JsonObject json) {
+ // check if current version the same
+ if (json.get(BPConstants.KEY_INVENTORY_VERSION).getAsInt() != BPConstants.INVENTORY_VERSION) {
+ throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
+ }
+
+ int dataVersion = json.get(BPConstants.KEY_INVENTORY_DATA_VERSION).getAsInt();
+ int itemsSize = json.get(BPConstants.KEY_INVENTORY_ITEMS_SIZE).getAsInt();
+
+ List items = new ArrayList<>();
+
+ JsonArray jsonItems = json.get(BPConstants.KEY_INVENTORY_ITEMS).getAsJsonArray();
+ for (JsonElement item : jsonItems) {
+ Dynamic dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
+ Dynamic dynamicItemFixed = DataFixers.getDataFixer()
+ .update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
+
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
+ .getOrThrow();
+
+ ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
+ int slot = item.getAsJsonObject().get(BPConstants.KEY_INVENTORY_SLOT).getAsInt();
+
+ items.add(new ItemStackWithSlot(slot, bukkitItem));
+ }
+
+ return new ItemStackContainerResult(itemsSize, items);
+ }
+
+ @Override
+ public JsonObject migrateToJsonElement(byte[] binary) {
+ CompoundTag compound;
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
+ compound = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to parse binary to nbt", e);
+ }
+
+ ListTag list = compound.getListOrEmpty("i");
+
+ int currentSlot = 0;
+
+ JsonArray jsonItems = new JsonArray();
+ for (Tag base : list) {
+ if (base instanceof CompoundTag itemTag) {
+ String itemType = itemTag.getString("id").orElse("");
+ if (itemType.equals("minecraft:air")) {
+ currentSlot++;
+ continue;
+ }
+
+ Dynamic dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
+ net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
+ .parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
+ .getOrThrow();
+
+ DataResult result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
+ JsonObject resultJson = result.getOrThrow().getAsJsonObject();
+
+ resultJson.addProperty(BPConstants.KEY_INVENTORY_SLOT, currentSlot);
+ jsonItems.add(resultJson);
+
+ currentSlot++;
+ }
+ }
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_INVENTORY_VERSION, BPConstants.INVENTORY_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_DATA_VERSION, DATA_VERSION);
+ json.addProperty(BPConstants.KEY_INVENTORY_ITEMS_SIZE, list.size());
+ json.add(BPConstants.KEY_INVENTORY_ITEMS, jsonItems);
+ return json;
+ }
+
+ @Override
+ public void setSkullProfile(SkullMeta meta, String texture) {
+ try {
+ GameProfile gameProfile = new GameProfile(UUID.randomUUID(), "");
+ gameProfile.getProperties().put("textures", new Property("textures", texture));
+
+ SET_PROFILE.accept(meta, gameProfile);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean isAir(Material material) {
+ return material == null || material == Material.AIR;
+ }
+}
\ No newline at end of file
diff --git a/zip-plugin/pom.xml b/zip-plugin/pom.xml
index e52f7a3..7aa6882 100644
--- a/zip-plugin/pom.xml
+++ b/zip-plugin/pom.xml
@@ -113,5 +113,11 @@
${revision}
compile
+
+ net.imprex
+ zip-nms-v1_21_R5
+ ${revision}
+ compile
+
\ No newline at end of file
diff --git a/zip-plugin/src/main/java/net/imprex/zip/Backpack.java b/zip-plugin/src/main/java/net/imprex/zip/Backpack.java
index 4a2c20f..0ae87ce 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/Backpack.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/Backpack.java
@@ -1,5 +1,9 @@
package net.imprex.zip;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -13,13 +17,21 @@
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
import net.imprex.zip.api.ZIPBackpack;
-import net.imprex.zip.common.Ingrim4Buffer;
+import net.imprex.zip.common.BPConstants;
import net.imprex.zip.common.UniqueId;
+import net.imprex.zip.common.ZIPLogger;
import net.imprex.zip.config.MessageConfig;
import net.imprex.zip.config.MessageKey;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
+import net.imprex.zip.nms.api.ItemStackWithSlot;
public class Backpack implements ZIPBackpack {
+
+ static final Gson GSON = new Gson();
private final BackpackHandler backpackHandler;
private final MessageConfig messageConfig;
@@ -55,7 +67,7 @@ public Backpack(BackpackPlugin plugin, BackpackType type, UniqueId id) {
this.save();
}
- public Backpack(BackpackPlugin plugin, UniqueId id, Ingrim4Buffer buffer) {
+ public Backpack(BackpackPlugin plugin, UniqueId id, JsonObject json) {
this.backpackHandler = plugin.getBackpackHandler();
this.messageConfig = plugin.getBackpackConfig().message();
this.identifierKey = plugin.getBackpackIdentifierKey();
@@ -65,14 +77,16 @@ public Backpack(BackpackPlugin plugin, UniqueId id, Ingrim4Buffer buffer) {
* Load backpack id from buffer but don't use it!
* Just for later migration to SQL
*/
- buffer.readByteArray();
+
+
this.id = id;
- this.typeRaw = buffer.readString();
+ this.typeRaw = json.get(BPConstants.KEY_TYPE_RAW).getAsString();
this.type = plugin.getBackpackRegistry().getTypeByName(this.typeRaw);
- byte[] contentAsByteArray = buffer.readByteArray();
- ItemStack[] content = NmsInstance.binaryToItemStack(contentAsByteArray).toArray(ItemStack[]::new);
+ JsonObject contentAsJson = json.getAsJsonObject(BPConstants.KEY_INVENTORY);
+ ItemStackContainerResult contentResult = NmsInstance.jsonElementToItemStack(contentAsJson);
+ ItemStack[] content = this.parseItemStackList(contentResult);
this.content = content;
if (this.type != null) {
@@ -97,8 +111,70 @@ public Backpack(BackpackPlugin plugin, UniqueId id, Ingrim4Buffer buffer) {
this.backpackHandler.registerBackpack(this);
}
+
+ private ItemStack[] parseItemStackList(ItemStackContainerResult result) {
+ int containerSize = result.containerSize();
+
+ ItemStack[] items = new ItemStack[containerSize];
+ Arrays.fill(items, new ItemStack(Material.AIR));
+
+ List duplicateSlot = null;
+
+ for (ItemStackWithSlot itemWithSlot : result.items()) {
+ ItemStack item = itemWithSlot.item();
+ int slot = itemWithSlot.slot();
+
+ if (containerSize <= slot) {
+ // something went wrong !? maybe user modified it him self
+ ZIPLogger.warn("Slot size was extended from " + containerSize + " to " + slot + " this should not happen. Do not change the slot number inside the config manually!?");
+
+ ItemStack[] newItems = new ItemStack[slot + 1];
+ System.arraycopy(items, 0, newItems, 0, items.length);
+ Arrays.fill(newItems, items.length, newItems.length, new ItemStack(Material.AIR));
+ items = newItems;
+ }
+
+ if (items[slot].getType() != Material.AIR) {
+ if (duplicateSlot == null) {
+ duplicateSlot = new ArrayList<>();
+ }
+ duplicateSlot.add(item);
+ ZIPLogger.warn("Duplicate item found on slot " + slot + " this should not happen. Do not change the slot number inside the config manually!?");
+ } else {
+ items[slot] = item;
+ }
+ }
+
+ // fill existing empty slots with duplicate item
+ while (duplicateSlot != null && !duplicateSlot.isEmpty()) {
+ outher: for (Iterator iterator = duplicateSlot.iterator(); iterator.hasNext();) {
+ ItemStack itemStack = (ItemStack) iterator.next();
+
+ for (int i = 0; i < items.length; i++) {
+ if (items[i].getType() == Material.AIR) {
+ items[i] = itemStack;
+ iterator.remove();
+ break;
+ } else if (i == items.length - 1) {
+ break outher;
+ }
+ }
+ }
- public void save(Ingrim4Buffer buffer) {
+ // extend slot limit and try again
+ if (!duplicateSlot.isEmpty()) {
+ int extendedSlots = items.length + duplicateSlot.size();
+ ItemStack[] newItems = new ItemStack[extendedSlots];
+ System.arraycopy(items, 0, newItems, 0, items.length);
+ Arrays.fill(newItems, items.length, newItems.length, new ItemStack(Material.AIR));
+ items = newItems;
+ }
+ }
+
+ return items;
+ }
+
+ public void save(JsonObject json) {
if (this.inventory != null) {
for (int i = 0; i < this.inventory.getSize(); i++) {
this.content[i] = this.inventory.getItem(i);
@@ -107,9 +183,10 @@ public void save(Ingrim4Buffer buffer) {
throw new NullPointerException("content can not be null");
}
- buffer.writeByteArray(this.id.toByteArray());
- buffer.writeString(this.typeRaw);
- buffer.writeByteArray(NmsInstance.itemstackToBinary(this.content));
+ json.addProperty(BPConstants.KEY_VERSION, BPConstants.VERSION);
+ json.addProperty(BPConstants.KEY_ID, this.id.toString());
+ json.addProperty(BPConstants.KEY_TYPE_RAW, this.typeRaw);
+ json.add(BPConstants.KEY_INVENTORY, NmsInstance.itemstackToJsonElement(this.content));
}
@Override
@@ -203,6 +280,8 @@ public boolean giveUnsueableContent(Player player) {
this.content[i] = null;
}
}
+
+ this.save();
return empty;
}
diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackHandler.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackHandler.java
index 5dd7c2d..a39970c 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/BackpackHandler.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackHandler.java
@@ -3,6 +3,9 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -17,16 +20,14 @@
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
-import com.google.common.io.ByteStreams;
+import com.google.gson.JsonObject;
-import io.netty.buffer.Unpooled;
import net.imprex.zip.api.ZIPBackpack;
import net.imprex.zip.api.ZIPBackpackType;
import net.imprex.zip.api.ZIPHandler;
import net.imprex.zip.api.ZIPUniqueId;
-import net.imprex.zip.common.Ingrim4Buffer;
import net.imprex.zip.common.UniqueId;
-import net.imprex.zip.util.ZIPLogger;
+import net.imprex.zip.common.ZIPLogger;
public class BackpackHandler implements ZIPHandler {
@@ -65,17 +66,25 @@ public void disable() {
}
private Backpack loadBackpack(UniqueId id) {
- Path file = this.folderPath.resolve(id.toString());
+ Path file = this.getPathForId(id);
if (!Files.isRegularFile(file)) {
- return null;
+ // migrate backpack to json
+ if (!BackpackMigrator.migrate(this.folderPath, id)) {
+ return null;
+ }
+
+ // backpack was successful migrated
}
- try (FileInputStream inputStream = new FileInputStream(file.toFile())) {
- byte[] data = ByteStreams.toByteArray(inputStream);
- Ingrim4Buffer buffer = new Ingrim4Buffer(Unpooled.wrappedBuffer(data));
-
- Backpack backpack = new Backpack(this.plugin, id, buffer);
+ try {
+ JsonObject json;
+ try (FileInputStream inputStream = new FileInputStream(file.toFile());
+ InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
+ json = Backpack.GSON.fromJson(inputStreamReader, JsonObject.class);
+ }
+
+ Backpack backpack = new Backpack(this.plugin, id, json);
return backpack;
} catch (Exception e) {
ZIPLogger.error("Unable to load backpack for id '" + file.getFileName().toString() + "'", e);
@@ -92,15 +101,18 @@ public void save(ZIPBackpack backpack) {
e.printStackTrace();
}
}
+
+ JsonObject json = new JsonObject();
+ if (backpack instanceof Backpack internalBackpack) {
+ internalBackpack.save(json);
+ } else {
+ throw new IllegalArgumentException("Backpack object is not a ZIPBackpack");
+ }
- Path file = this.folderPath.resolve(backpack.getId().toString());
- try (FileOutputStream outputStream = new FileOutputStream(file.toFile())) {
- Ingrim4Buffer buffer = new Ingrim4Buffer(Unpooled.buffer());
- ((Backpack) backpack).save(buffer);
-
- byte[] bytes = new byte[buffer.readableBytes()];
- buffer.readBytes(bytes);
- outputStream.write(bytes);
+ Path file = this.getPathForId(backpack.getId());
+ try (FileOutputStream outputStream = new FileOutputStream(file.toFile());
+ OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) {
+ Backpack.GSON.toJson(json, outputStreamWriter);
} catch (IOException e) {
e.printStackTrace();
}
@@ -154,6 +166,10 @@ public Backpack getBackpack(ItemStack item) {
}
}
+ if (this.loadingIssue.contains(uniqueId)) {
+ return null;
+ }
+
if (dataContainer.has(this.backpackIdentifierKey, PersistentDataType.STRING)) {
String backpackIdentifier = dataContainer.get(this.backpackIdentifierKey, PersistentDataType.STRING);
BackpackType backpackType = this.registry.getTypeByName(backpackIdentifier);
@@ -201,4 +217,27 @@ public boolean isBackpack(ItemStack item) {
return false;
}
+
+ @Override
+ public UniqueId getUniqueId(ItemStack item) {
+ if (item != null && item.hasItemMeta()) {
+ ItemMeta meta = item.getItemMeta();
+ PersistentDataContainer dataContainer = meta.getPersistentDataContainer();
+
+ if (dataContainer.has(this.backpackStorageKey, PersistentDataType.BYTE_ARRAY)) {
+ byte[] storageKey = dataContainer.get(this.backpackStorageKey, PersistentDataType.BYTE_ARRAY);
+ return UniqueId.fromByteArray(storageKey);
+ }
+ }
+
+ return null;
+ }
+
+ private Path getPathForId(ZIPUniqueId id) {
+ return this.folderPath.resolve(id.toString() + ".json");
+ }
+
+ public Path getFolderPath() {
+ return this.folderPath;
+ }
}
\ No newline at end of file
diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackListener.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackListener.java
index da489c0..7bc4d03 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/BackpackListener.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackListener.java
@@ -19,6 +19,7 @@
import org.bukkit.inventory.ItemStack;
import net.imprex.zip.api.ZIPBackpackType;
+import net.imprex.zip.common.UniqueId;
import net.imprex.zip.config.MessageConfig;
import net.imprex.zip.config.MessageKey;
@@ -114,7 +115,9 @@ public void onPlayerInteract(PlayerInteractEvent event) {
if (backpack != null) {
backpack.open(event.getPlayer());
} else {
- this.messageConfig.send(event.getPlayer(), MessageKey.UnableToLoadBackpack);
+ UniqueId id = this.backpackHandler.getUniqueId(event.getItem());
+ String idString = id != null ? id.toString() : "NOT READABLE";
+ this.messageConfig.send(event.getPlayer(), MessageKey.UnableToLoadBackpack, idString);
}
}
}
diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackMigrator.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackMigrator.java
new file mode 100644
index 0000000..774906d
--- /dev/null
+++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackMigrator.java
@@ -0,0 +1,125 @@
+package net.imprex.zip;
+
+import java.awt.geom.IllegalPathStateException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+import com.google.common.io.ByteStreams;
+import com.google.gson.JsonObject;
+
+import io.netty.buffer.Unpooled;
+import net.imprex.zip.common.BPConstants;
+import net.imprex.zip.common.Ingrim4Buffer;
+import net.imprex.zip.common.UniqueId;
+import net.imprex.zip.common.ZIPLogger;
+
+public class BackpackMigrator {
+
+ /*
+ * Previous implementation
+ *
+ * LOAD
+ * read id buffer.readByteArray();
+ * read typeRaw buffer.readString();
+ * read content buffer.readByteArray();
+ *
+ * SAVE
+ * buffer.writeByteArray(this.id.toByteArray());
+ * buffer.writeString(this.typeRaw);
+ * buffer.writeByteArray(NmsInstance.itemstackToBinary(this.content));
+ */
+
+ public static void checkForMigrations(Path folderPath) {
+ long startTime = System.currentTimeMillis();
+ int statisticSuccessful = 0;
+ int statisticFailed = 0;
+
+ ZIPLogger.info("Checking for migration data...");
+
+ try (Stream stream = Files.walk(folderPath, 1)) {
+ Path[] paths = stream
+ .filter(file -> !Files.isDirectory(file))
+ .filter(file -> Files.isRegularFile(file))
+ .filter(file -> !file.getFileName().toString().endsWith(".json"))
+ .toArray(Path[]::new);
+
+ ZIPLogger.info("Migration found " + paths.length + " outdated backpacks.");
+
+ if (paths.length == 0) {
+ return;
+ }
+
+ for (Path file : paths) {
+ try {
+ UniqueId id = UniqueId.fromString(file.getFileName().toString());
+ if (BackpackMigrator.migrate(folderPath, id)) {
+ statisticSuccessful++;
+ } else {
+ statisticFailed++;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ statisticFailed++;
+ }
+ }
+
+ ZIPLogger.info(String.format("Migration finished. %d/%d backpacks migrated and %d failed to migrate in %d seconds.",
+ paths.length,
+ statisticSuccessful,
+ statisticFailed,
+ Math.round((System.currentTimeMillis() - startTime) / 1000)));
+ } catch (IOException e) {
+ ZIPLogger.error("Error when migrating backpacks", e);
+ }
+ }
+
+ public static boolean migrate(Path folderPath, UniqueId id) {
+ try {
+ Path previousFile = folderPath.resolve(id.toString());
+
+ if (!Files.isRegularFile(previousFile)) {
+ return false;
+ }
+
+ Ingrim4Buffer buffer;
+ try (FileInputStream inputStream = new FileInputStream(previousFile.toFile())) {
+ byte[] data = ByteStreams.toByteArray(inputStream);
+ buffer = new Ingrim4Buffer(Unpooled.wrappedBuffer(data));
+ }
+
+ byte[] previousId = buffer.readByteArray();
+ String previousRawType = buffer.readString();
+ byte[] previousContent = buffer.readByteArray();
+
+ JsonObject json = new JsonObject();
+ json.addProperty(BPConstants.KEY_VERSION, BPConstants.VERSION);
+ json.addProperty(BPConstants.KEY_ID, UniqueId.fromByteArray(previousId).toString());
+ json.addProperty(BPConstants.KEY_TYPE_RAW, previousRawType);
+ json.add(BPConstants.KEY_INVENTORY, NmsInstance.migrateToJsonElement(previousContent));
+
+ Path newFile = folderPath.resolve(id.toString() + ".json");
+ if (Files.exists(newFile)) {
+ throw new IllegalPathStateException("File path for migration " + id.toString() + " already exist!");
+ }
+
+ try (FileOutputStream outputStream = new FileOutputStream(newFile.toFile());
+ OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) {
+ Backpack.GSON.toJson(json, outputStreamWriter);
+ }
+
+ Files.deleteIfExists(previousFile);
+
+ ZIPLogger.info("Successful migrated backpack id '" + id.toString() + "'");
+ return true;
+ } catch (Exception e) {
+ ZIPLogger.error("Unable to migrate backpack id '" + id.toString() + "'", e);
+ }
+ return false;
+ }
+}
diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackPlugin.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackPlugin.java
index 2d4d5a2..2f9fdb5 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/BackpackPlugin.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackPlugin.java
@@ -17,8 +17,8 @@
import net.imprex.zip.api.ZIPService;
import net.imprex.zip.command.BackpackCommand;
+import net.imprex.zip.common.ZIPLogger;
import net.imprex.zip.config.BackpackConfig;
-import net.imprex.zip.util.ZIPLogger;
public class BackpackPlugin extends JavaPlugin implements Listener, ZIPService {
@@ -45,6 +45,8 @@ public void onLoad() {
public void onEnable() {
try {
NmsInstance.initialize();
+
+ BackpackMigrator.checkForMigrations(this.backpackHandler.getFolderPath());
this.backpackConfig.deserialize();
diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackRegistry.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackRegistry.java
index fb90f96..9a0f8c0 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/BackpackRegistry.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackRegistry.java
@@ -12,10 +12,10 @@
import net.imprex.zip.api.ZIPBackpackType;
import net.imprex.zip.api.ZIPRecipe;
import net.imprex.zip.api.ZIPRegistry;
+import net.imprex.zip.common.ZIPLogger;
import net.imprex.zip.config.BackpackConfig;
import net.imprex.zip.config.BackpackTypeConfig;
import net.imprex.zip.config.BackpackTypeListConfig;
-import net.imprex.zip.util.ZIPLogger;
public class BackpackRegistry implements ZIPRegistry {
diff --git a/zip-plugin/src/main/java/net/imprex/zip/NmsInstance.java b/zip-plugin/src/main/java/net/imprex/zip/NmsInstance.java
index d4afe3e..1232970 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/NmsInstance.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/NmsInstance.java
@@ -2,15 +2,17 @@
package net.imprex.zip;
import java.lang.reflect.Constructor;
-import java.util.List;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
+import com.google.gson.JsonObject;
+
import net.imprex.zip.common.MinecraftVersion;
+import net.imprex.zip.common.ZIPLogger;
+import net.imprex.zip.nms.api.ItemStackContainerResult;
import net.imprex.zip.nms.api.NmsManager;
-import net.imprex.zip.util.ZIPLogger;
public class NmsInstance {
@@ -38,12 +40,16 @@ public static void initialize() {
ZIPLogger.info("NMS adapter for server version \"" + nmsVersion + "\" found!");
}
- public static byte[] itemstackToBinary(ItemStack[] items) {
- return instance.itemstackToBinary(items);
+ public static JsonObject itemstackToJsonElement(ItemStack[] items) {
+ return instance.itemstackToJsonElement(items);
+ }
+
+ public static ItemStackContainerResult jsonElementToItemStack(JsonObject jsonElement) {
+ return instance.jsonElementToItemStack(jsonElement);
}
- public static List binaryToItemStack(byte[] binary) {
- return instance.binaryToItemStack(binary);
+ public static JsonObject migrateToJsonElement(byte[] binary) {
+ return instance.migrateToJsonElement(binary);
}
public static void setSkullProfile(SkullMeta meta, String texture) {
diff --git a/zip-plugin/src/main/java/net/imprex/zip/UpdateSystem.java b/zip-plugin/src/main/java/net/imprex/zip/UpdateSystem.java
index b650306..a8cc6f4 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/UpdateSystem.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/UpdateSystem.java
@@ -1,59 +1,66 @@
+/**
+ * @author Imprex-Development
+ * @see UpdateSystem.java
+ */
package net.imprex.zip;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
+import com.google.gson.annotations.SerializedName;
+import net.imprex.zip.common.MinecraftVersion;
+import net.imprex.zip.common.Version;
+import net.imprex.zip.common.ZIPLogger;
import net.imprex.zip.config.GeneralConfig;
import net.imprex.zip.config.MessageConfig;
import net.imprex.zip.config.MessageKey;
-import net.imprex.zip.util.ZIPLogger;
+import net.imprex.zip.util.AbstractHttpService;
+import net.imprex.zip.util.ConsoleUtil;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
-/**
- * @author Imprex-Development
- * @see UpdateSystem.java
- */
-public class UpdateSystem {
-
- private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(?:-b(\\d+))?");
+public class UpdateSystem extends AbstractHttpService {
- private static final String API_LATEST = "https://api.github.com/repos/Imprex-Development/zero-inventory-problems/releases/latest";
- private static final long UPDATE_COOLDOWN = 1_800_000L; // 30min
+ private static final Pattern DEV_VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(?:-b(?\\d+))?");
- private static final String repeatString(String message, int repeat) {
- StringBuilder stringBuilder = new StringBuilder();
- for (int i = 0; i < repeat; i++) {
- stringBuilder.append(message);
- }
- return stringBuilder.toString();
+ private static boolean isDevVersion(String version) {
+ Matcher matcher = DEV_VERSION_PATTERN.matcher(version);
+ return matcher.find() && matcher.group("build") != null;
}
- private final Lock lock = new ReentrantLock();
+ private static final String API_URI = "https://api.modrinth.com/v2/project/zero-inventory-problems-zip-backpacks/version?loaders=%s&game_versions=%s";
+ private static final String DOWNLOAD_URI = "https://modrinth.com/plugin/zero-inventory-problems-zip-backpacks/version/%s";
+
+ private static final Duration CACHE_DURATION = Duration.ofMinutes(10L);
private final BackpackPlugin plugin;
private final GeneralConfig generalConfig;
private final MessageConfig messageConfig;
- private JsonObject releaseData;
- private long updateCooldown = -1;
- private int failedAttempts = 0;
+ private final AtomicReference validUntil = new AtomicReference<>();
+ private final AtomicReference>> latestVersion = new AtomicReference<>();
public UpdateSystem(BackpackPlugin plugin) {
+ super(plugin);
+
this.plugin = plugin;
this.generalConfig = plugin.getBackpackConfig().general();
this.messageConfig = plugin.getBackpackConfig().message();
@@ -61,98 +68,105 @@ public UpdateSystem(BackpackPlugin plugin) {
this.checkForUpdates();
}
- private JsonObject getReleaseData() {
- this.lock.lock();
- try {
- long systemTime = System.currentTimeMillis();
-
- if (this.failedAttempts < 5) {
-
- if (this.releaseData != null || systemTime - this.updateCooldown > UPDATE_COOLDOWN) {
- try {
- URL url = new URL(API_LATEST);
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- try (InputStreamReader inputStreamReader = new InputStreamReader(connection.getInputStream())) {
- this.releaseData = JsonParser.parseReader(inputStreamReader).getAsJsonObject();
- this.updateCooldown = systemTime;
- }
- } catch (IOException e) {
- ZIPLogger.warn("Unable to fetch latest update from: " + API_LATEST);
- ZIPLogger.warn(e.toString());
-
- if (++this.failedAttempts == 5) {
- this.updateCooldown = systemTime;
- }
- }
- }
-
- } else if (systemTime - this.updateCooldown > UPDATE_COOLDOWN) {
- this.failedAttempts = 0;
- this.updateCooldown = -1;
- return this.getReleaseData();
- }
-
- return this.releaseData;
- } finally {
- this.lock.unlock();
+ private CompletableFuture> requestLatestVersion() {
+ String installedVersion = this.plugin.getDescription().getVersion();
+ if (!this.generalConfig.checkForUpdates || isDevVersion(installedVersion)) {
+ ZIPLogger.debug("UpdateSystem - Update check disabled or dev version detected; skipping");
+ return CompletableFuture.completedFuture(Optional.empty());
}
- }
- private String getTagName() {
- JsonObject releaseData = this.getReleaseData();
- if (releaseData != null && releaseData.has("tag_name")) {
- return releaseData.getAsJsonPrimitive("tag_name").getAsString();
- }
- return null;
+ var uri = String.format(API_URI, "bukkit", MinecraftVersion.current());
+ return HTTP.sendAsync(request(uri).build(), json(ModrinthVersion[].class)).thenApply(request -> {
+ var version = Version.parse(installedVersion);
+ var latestVersion = Arrays.stream(request.body())
+ .filter(e -> Objects.equals(e.versionType, "release"))
+ .filter(e -> Objects.equals(e.status, "listed"))
+ .sorted(Comparator.reverseOrder())
+ .findFirst();
+
+ latestVersion.ifPresentOrElse(
+ v -> ZIPLogger.debug("UpdateSystem - Fetched latest version " + v.version),
+ () -> ZIPLogger.debug("UpdateSystem - Couldn't fetch latest version"));
+
+ return latestVersion.map(v -> version.isBelow(v.version) ? v : null);
+ }).exceptionally(throwable -> {
+ ZIPLogger.warn("UpdateSystem - Unable to fetch latest version", throwable);
+ return Optional.empty();
+ });
}
- private String getHtmlUrl() {
- JsonObject releaseData = this.getReleaseData();
- if (releaseData != null && releaseData.has("html_url")) {
- return releaseData.getAsJsonPrimitive("html_url").getAsString();
+ private CompletableFuture> getLatestVersion() {
+ Instant validUntil = this.validUntil.get();
+ if (validUntil != null && validUntil.compareTo(Instant.now()) < 0 && this.validUntil.compareAndSet(validUntil, null)) {
+ ZIPLogger.debug("UpdateSystem - Cleared latest cached version");
+ this.latestVersion.set(null);
}
- return null;
- }
- private boolean isDevVersion(String version) {
- Matcher matcher = VERSION_PATTERN.matcher(version);
- return matcher.find() && matcher.groupCount() == 4;
+ CompletableFuture> existingFuture = this.latestVersion.get();
+ if (existingFuture != null) {
+ return existingFuture;
+ }
+
+ CompletableFuture> newFuture = new CompletableFuture<>();
+ if (this.latestVersion.compareAndSet(null, newFuture)) {
+ ZIPLogger.debug("UpdateSystem - Starting to check for updates");
+ this.requestLatestVersion().thenAccept(version -> {
+ this.validUntil.set(Instant.now().plus(CACHE_DURATION));
+ newFuture.complete(version);
+ });
+ return newFuture;
+ } else {
+ return this.latestVersion.get();
+ }
}
- private boolean isUpdateAvailable() {
- String version = this.plugin.getDescription().getVersion();
- if (this.generalConfig.checkForUpdates && !this.isDevVersion(version)) {
- String tagName = this.getTagName();
- return tagName != null && !version.equals(tagName);
- }
- return false;
+ private void ifNewerVersionAvailable(Consumer consumer) {
+ this.getLatestVersion().thenAccept(o -> o.ifPresent(consumer));
}
private void checkForUpdates() {
- Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
- if (this.isUpdateAvailable()) {
- String url = " " + this.getHtmlUrl() + " ";
- int lineLength = (int) Math.ceil((url.length() - 18) / 2d);
- String line = repeatString("=", lineLength);
-
- ZIPLogger.warn(line + " Update available " + line);
- ZIPLogger.warn(url);
- ZIPLogger.warn(repeatString("=", lineLength * 2 + 18));
- }
+ this.ifNewerVersionAvailable(version -> {
+ String downloadUri = String.format(DOWNLOAD_URI, version.version);
+ ConsoleUtil.printBox(Level.WARNING, "UPDATE AVAILABLE", "", downloadUri);
});
}
public void checkForUpdates(Player player) {
- Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
- if (this.isUpdateAvailable()) {
- BaseComponent[] components = new ComponentBuilder(String.format("%s%s ", this.messageConfig.get(MessageKey.ANewReleaseIsAvailable)))
- .append(this.messageConfig.getWithoutPrefix(MessageKey.ClickHere))
- .event(new ClickEvent(ClickEvent.Action.OPEN_URL, this.getHtmlUrl()))
- .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(this.messageConfig.getWithoutPrefix(MessageKey.ClickHereToSeeTheLatestRelease)))).create();
- Bukkit.getScheduler().runTask(this.plugin, () -> {
- player.spigot().sendMessage(components);
- });
- }
+ this.ifNewerVersionAvailable(version -> {
+ String downloadUri = String.format(DOWNLOAD_URI, version.version);
+ BaseComponent[] components = new ComponentBuilder(String.format("%s%s ", this.messageConfig.get(MessageKey.ANewReleaseIsAvailable)))
+ .append(this.messageConfig.getWithoutPrefix(MessageKey.ClickHere))
+ .event(new ClickEvent(ClickEvent.Action.OPEN_URL, downloadUri))
+ .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(new ComponentBuilder(this.messageConfig.getWithoutPrefix(MessageKey.ClickHereToSeeTheLatestRelease)).create()))).create();
+ Bukkit.getScheduler().runTask(this.plugin, () -> {
+ player.spigot().sendMessage(components);
+ });
});
}
-}
\ No newline at end of file
+
+ public static class ModrinthVersion implements Comparable {
+
+ private static final Comparator COMPARATOR =
+ Comparator.comparing(e -> e.version, Comparator.nullsLast(Version::compareTo));
+
+ @SerializedName("version_number")
+ public Version version;
+
+ @SerializedName("game_versions")
+ public List gameVersions;
+
+ @SerializedName("version_type")
+ public String versionType;
+
+ @SerializedName("loaders")
+ public List loaders;
+
+ @SerializedName("status")
+ public String status;
+
+ @Override
+ public int compareTo(ModrinthVersion other) {
+ return COMPARATOR.compare(this, other);
+ }
+ }
+}
diff --git a/zip-plugin/src/main/java/net/imprex/zip/command/LinkCommand.java b/zip-plugin/src/main/java/net/imprex/zip/command/LinkCommand.java
index 49dbe18..da4f1e3 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/command/LinkCommand.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/command/LinkCommand.java
@@ -71,6 +71,9 @@ public void onCommand(CommandSender sender, String[] args) {
} else if (linkingBackpack.equals(backpack)) {
this.messageConfig.send(player, MessageKey.ThisBackpackIsAlreadyLinkedThoThat);
return;
+ } else if (item.getAmount() > 1) {
+ this.messageConfig.send(player, MessageKey.StackedBackpacksCanNotBeLinked);
+ return;
}
linkingBackpack.applyOnItem(item);
diff --git a/zip-plugin/src/main/java/net/imprex/zip/config/MessageConfig.java b/zip-plugin/src/main/java/net/imprex/zip/config/MessageConfig.java
index aa40f5a..bf81c99 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/config/MessageConfig.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/config/MessageConfig.java
@@ -20,7 +20,7 @@
import org.bukkit.configuration.file.YamlConfiguration;
import net.imprex.zip.BackpackPlugin;
-import net.imprex.zip.util.ZIPLogger;
+import net.imprex.zip.common.ZIPLogger;
import net.md_5.bungee.api.ChatColor;
public class MessageConfig {
diff --git a/zip-plugin/src/main/java/net/imprex/zip/config/MessageKey.java b/zip-plugin/src/main/java/net/imprex/zip/config/MessageKey.java
index 50e99e5..bf4bfeb 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/config/MessageKey.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/config/MessageKey.java
@@ -54,13 +54,14 @@ public enum MessageKey {
YourBackpackLinkRequestWasCancelled("yourBackpackLinkRequestWasCancelled", "Your backpack link request was cancelled"),
BothBackpacksNeedToBeTheSameType("bothBackpacksNeedToBeTheSameType", "Both Backpacks need to be the same type"),
ThisBackpackIsAlreadyLinkedThoThat("thisBackpackIsAlreadyLinkedThoThat", "This backpack is already linked to that backpack"),
+ StackedBackpacksCanNotBeLinked("stackedBackpacksCanNotBeLinked", "Stacked backpacks can not be linked at the same time"),
PleaseEnterANumber("pleaseEnterANumber", "Please enter a number"),
EnterANumberBetweenArgsAndArgs("enterANumberBetweenArgsAndArgs", "Please enter a number between {0} and {1}"),
LoreLineCreate("loreLineCreate", "The lore line {0} was added"),
LoreLineChange("loreLineChange", "The lore line {0} was changed"),
LoreLineDelete("loreLineDelete", "The lore line {0} was deleted"),
MaxLoreCountReached("maxLoreCountReached", "You have reached the max lore count of {0}"),
- UnableToLoadBackpack("unableToLoadBackpack", "Backpack can't be loaded!");
+ UnableToLoadBackpack("unableToLoadBackpack", "Backpack id §8\"§e{0}§8\" §7can't be loaded!");
public static MessageKey findByKey(String key) {
for (MessageKey messageKey : values()) {
diff --git a/zip-plugin/src/main/java/net/imprex/zip/config/RecipeConfig.java b/zip-plugin/src/main/java/net/imprex/zip/config/RecipeConfig.java
index 99d4d3c..67c335b 100644
--- a/zip-plugin/src/main/java/net/imprex/zip/config/RecipeConfig.java
+++ b/zip-plugin/src/main/java/net/imprex/zip/config/RecipeConfig.java
@@ -6,7 +6,7 @@
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
-import net.imprex.zip.util.ZIPLogger;
+import net.imprex.zip.common.ZIPLogger;
public class RecipeConfig {
diff --git a/zip-plugin/src/main/java/net/imprex/zip/util/AbstractHttpService.java b/zip-plugin/src/main/java/net/imprex/zip/util/AbstractHttpService.java
new file mode 100644
index 0000000..2c0fee8
--- /dev/null
+++ b/zip-plugin/src/main/java/net/imprex/zip/util/AbstractHttpService.java
@@ -0,0 +1,56 @@
+/**
+ * @author Imprex-Development
+ * @see AbstractHttpService.java
+ */
+package net.imprex.zip.util;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse.BodyHandler;
+import java.net.http.HttpResponse.BodySubscribers;
+
+import org.bukkit.plugin.PluginDescriptionFile;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import net.imprex.zip.BackpackPlugin;
+import net.imprex.zip.common.Version;
+
+public abstract class AbstractHttpService {
+
+ public static final Gson GSON = new GsonBuilder().setPrettyPrinting()
+ .registerTypeAdapter(Version.class, new Version.Json())
+ .create();
+
+ public static final HttpClient HTTP = HttpClient.newHttpClient();
+
+ protected final String userAgent;
+
+ public AbstractHttpService(BackpackPlugin plugin) {
+ PluginDescriptionFile pluginDescription = plugin.getDescription();
+ this.userAgent = String.format("%s/%s", pluginDescription.getName(), pluginDescription.getVersion());
+ }
+
+ protected HttpRequest.Builder request(String url) {
+ return HttpRequest.newBuilder(URI.create(url))
+ .header("User-Agent", userAgent)
+ .header("Accept", "application/json");
+ }
+
+ protected static BodyHandler json(Class target) {
+ return (responseInfo) -> responseInfo.statusCode() == 200
+ ? BodySubscribers.mapping(BodySubscribers.ofInputStream(), inputStream -> {
+ try (InputStreamReader reader = new InputStreamReader(inputStream)) {
+ return GSON.fromJson(new InputStreamReader(inputStream), target);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ })
+ : BodySubscribers.replacing(null);
+ }
+}
diff --git a/zip-plugin/src/main/java/net/imprex/zip/util/ConsoleUtil.java b/zip-plugin/src/main/java/net/imprex/zip/util/ConsoleUtil.java
new file mode 100644
index 0000000..733aa52
--- /dev/null
+++ b/zip-plugin/src/main/java/net/imprex/zip/util/ConsoleUtil.java
@@ -0,0 +1,115 @@
+/**
+ * @author Imprex-Development
+ * @see ConsoleUtil.java
+ */
+package net.imprex.zip.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+
+import org.bukkit.ChatColor;
+
+import net.imprex.zip.common.ZIPLogger;
+
+public final class ConsoleUtil {
+
+ private static final int BOX_PADDING = 3;
+ private static final int BOX_PREFERRED_WIDTH = 48;
+
+ private ConsoleUtil() {
+ }
+
+ public static String replaceAnsiColorWithChatColor(String value) {
+ value = value.replaceAll("\u001B\\[m", ChatColor.RESET.toString());
+ value = value.replaceAll("\u001B\\[31;1m", ChatColor.RED.toString());
+ value = value.replaceAll("\u001B\\[33;1m", ChatColor.YELLOW.toString());
+ return value;
+ }
+
+ public static void printBox(Level level, String...lines) {
+ for (String line : createBox(lines)) {
+ ZIPLogger.log(level, line);
+ }
+ }
+
+ /**
+ * Creates a ASCII box around the given lines
+ */
+ public static Iterable createBox(String...lines) {
+
+ List wrappedLines = new ArrayList<>();
+ for (String line : lines) {
+ line = line.trim();
+
+ while (line.length() > BOX_PREFERRED_WIDTH) {
+
+ int splitLength = 0;
+ for (int i = 0; i < line.length(); i++) {
+ if (Character.isWhitespace(line.charAt(i))) {
+ if (i <= BOX_PREFERRED_WIDTH) {
+ splitLength = i;
+ } else {
+ break;
+ }
+ }
+ }
+
+ // can't split line no whitespace character found
+ if (splitLength == 0) {
+ break;
+ }
+
+ // split line at latest word that fit length
+ wrappedLines.add(line.substring(0, splitLength));
+ line = line.substring(splitLength, line.length()).trim();
+ }
+
+ // add remainder
+ wrappedLines.add(line);
+ }
+
+ // get max line width
+ int width = 0;
+ for (String line : wrappedLines) {
+ width = Math.max(width, line.length());
+ }
+
+ // add padding
+ int totalWidth = width + BOX_PADDING * 2;
+
+ // create top/bottom lines
+ String bottomTopLine = repeat('-', totalWidth);
+ String topLine = String.format("+%s+", bottomTopLine);
+ String bottomLine = String.format("+%s+", bottomTopLine);
+
+ // create box
+ List box = new ArrayList<>(wrappedLines.size() + 2);
+ box.add(topLine);
+
+ for (String line : wrappedLines) {
+ int space = totalWidth - line.length();
+
+ // center line
+ String leftPadding, rightPadding;
+ if (space % 2 == 0) {
+ leftPadding = rightPadding = repeat(' ', space / 2);
+ } else {
+ leftPadding = repeat(' ', space / 2 + 1);
+ rightPadding = repeat(' ', space / 2);
+ }
+
+ box.add(String.format("|%s%s%s|", leftPadding, line, rightPadding));
+ }
+
+ box.add(bottomLine);
+ return box;
+ }
+
+ private static String repeat(char character, int length) {
+ char[] string = new char[length];
+ Arrays.fill(string, character);
+ return new String(string);
+ }
+}
diff --git a/zip-plugin/src/main/resources/lang/en_US.yml b/zip-plugin/src/main/resources/lang/en_US.yml
index 22be703..5190ed2 100644
--- a/zip-plugin/src/main/resources/lang/en_US.yml
+++ b/zip-plugin/src/main/resources/lang/en_US.yml
@@ -45,10 +45,11 @@ youNeedToLinkABackpackFirst: "You need to link a backpack at first"
yourBackpackLinkRequestWasCancelled: "Your backpack link request was cancelled"
bothBackpacksNeedToBeTheSameType: "Both Backpacks need to be the same type"
thisBackpackIsAlreadyLinkedThoThat: "This backpack is already linked to that backpack"
+stackedBackpacksCanNotBeLinked: "Stacked backpacks can not be linked at the same time"
pleaseEnterANumber: "Please enter a number"
enterANumberBetweenArgsAndArgs: "Please enter a number between {0} and {1}"
loreLineCreate: "The lore line {0} was added"
loreLineChange: "The lore line {0} was changed"
loreLineDelete: "The lore line {0} was deleted"
maxLoreCountReached: "You have reached the max lore count of {0}"
-unableToLoadBackpack: "Backpack can't be loaded!"
\ No newline at end of file
+unableToLoadBackpack: "Backpack id &8\"&e{0}&8\" &7can't be loaded!"
\ No newline at end of file
diff --git a/zip-plugin/src/main/resources/plugin.yml b/zip-plugin/src/main/resources/plugin.yml
index e6e74b8..ea27ebc 100644
--- a/zip-plugin/src/main/resources/plugin.yml
+++ b/zip-plugin/src/main/resources/plugin.yml
@@ -31,6 +31,6 @@ permissions:
zeroinventoryproblems.type:
default: op
description: A list of all backpack types
- zeroinventoryproblems.lore:
+ zeroinventoryproblems.migrate:
default: op
- description: Change the lore of your current backpack
\ No newline at end of file
+ description: Migrate all existing backpacks to the new format
\ No newline at end of file