Skip to content

Commit 2f64594

Browse files
committed
feat: 1.21.6 support
feat: convert nbt into readable json binary
1 parent 4596272 commit 2f64594

File tree

4 files changed

+303
-1
lines changed

4 files changed

+303
-1
lines changed

.github/workflows/buildtools.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ checkVersion "1.20.6" "21"
3535
checkVersion "1.21" "21"
3636
checkVersion "1.21.3" "21"
3737
checkVersion "1.21.4" "21"
38-
checkVersion "1.21.5" "21"
38+
checkVersion "1.21.5" "21"
39+
checkVersion "1.21.6" "21"

zip-nms/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
<module>zip-nms-v1_21_R2</module>
2424
<module>zip-nms-v1_21_R3</module>
2525
<module>zip-nms-v1_21_R4</module>
26+
<module>zip-nms-v1_21_R5</module>
2627
</modules>
2728
</project>

zip-nms/zip-nms-v1_21_R5/pom.xml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
4+
<parent>
5+
<groupId>net.imprex</groupId>
6+
<artifactId>zip-nms</artifactId>
7+
<version>${revision}</version>
8+
</parent>
9+
10+
<artifactId>zip-nms-v1_21_R5</artifactId>
11+
12+
<dependencies>
13+
<dependency>
14+
<groupId>net.imprex</groupId>
15+
<artifactId>zip-nms-api</artifactId>
16+
<version>${revision}</version>
17+
<scope>provided</scope>
18+
</dependency>
19+
<dependency>
20+
<groupId>org.spigotmc</groupId>
21+
<artifactId>spigot</artifactId>
22+
<version>1.21.6-R0.1-SNAPSHOT</version>
23+
<classifier>remapped-mojang</classifier>
24+
<scope>provided</scope>
25+
</dependency>
26+
</dependencies>
27+
28+
<build>
29+
<plugins>
30+
<plugin>
31+
<groupId>net.md-5</groupId>
32+
<artifactId>specialsource-maven-plugin</artifactId>
33+
<version>${plugin.specialsource.version}</version>
34+
<executions>
35+
<execution>
36+
<phase>package</phase>
37+
<goals>
38+
<goal>remap</goal>
39+
</goals>
40+
<id>remap-obf</id>
41+
<configuration>
42+
<srgIn>
43+
org.spigotmc:minecraft-server:1.21.6-R0.1-SNAPSHOT:txt:maps-mojang</srgIn>
44+
<reverse>true</reverse>
45+
<remappedDependencies>
46+
org.spigotmc:spigot:1.21.6-R0.1-SNAPSHOT:jar:remapped-mojang</remappedDependencies>
47+
<remappedArtifactAttached>true</remappedArtifactAttached>
48+
<remappedClassifierName>remapped-obf</remappedClassifierName>
49+
</configuration>
50+
</execution>
51+
<execution>
52+
<phase>package</phase>
53+
<goals>
54+
<goal>remap</goal>
55+
</goals>
56+
<id>remap-spigot</id>
57+
<configuration>
58+
<inputFile>
59+
${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
60+
<srgIn>
61+
org.spigotmc:minecraft-server:1.21.6-R0.1-SNAPSHOT:csrg:maps-spigot</srgIn>
62+
<remappedDependencies>
63+
org.spigotmc:spigot:1.21.6-R0.1-SNAPSHOT:jar:remapped-obf</remappedDependencies>
64+
</configuration>
65+
</execution>
66+
</executions>
67+
</plugin>
68+
</plugins>
69+
</build>
70+
</project>
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
package net.imprex.zip.nms.v1_21_R5;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.ByteArrayOutputStream;
5+
import java.io.IOException;
6+
import java.io.InputStreamReader;
7+
import java.io.OutputStreamWriter;
8+
import java.lang.reflect.InvocationTargetException;
9+
import java.lang.reflect.Method;
10+
import java.nio.charset.StandardCharsets;
11+
import java.util.ArrayList;
12+
import java.util.Collections;
13+
import java.util.List;
14+
import java.util.UUID;
15+
import java.util.function.BiConsumer;
16+
17+
import org.bukkit.Material;
18+
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
19+
import org.bukkit.inventory.ItemStack;
20+
import org.bukkit.inventory.meta.SkullMeta;
21+
22+
import com.google.gson.Gson;
23+
import com.google.gson.JsonArray;
24+
import com.google.gson.JsonElement;
25+
import com.google.gson.JsonObject;
26+
import com.mojang.authlib.GameProfile;
27+
import com.mojang.authlib.properties.Property;
28+
import com.mojang.serialization.DataResult;
29+
import com.mojang.serialization.Dynamic;
30+
import com.mojang.serialization.DynamicOps;
31+
import com.mojang.serialization.JsonOps;
32+
33+
import net.imprex.zip.common.ReflectionUtil;
34+
import net.imprex.zip.nms.api.NmsManager;
35+
import net.minecraft.SharedConstants;
36+
import net.minecraft.core.RegistryAccess;
37+
import net.minecraft.nbt.CompoundTag;
38+
import net.minecraft.nbt.ListTag;
39+
import net.minecraft.nbt.NbtAccounter;
40+
import net.minecraft.nbt.NbtIo;
41+
import net.minecraft.nbt.NbtOps;
42+
import net.minecraft.nbt.Tag;
43+
import net.minecraft.server.MinecraftServer;
44+
import net.minecraft.util.datafix.DataFixers;
45+
import net.minecraft.util.datafix.fixes.References;
46+
import net.minecraft.world.item.component.ResolvableProfile;
47+
48+
public class ZipNmsManager implements NmsManager {
49+
50+
private static final BiConsumer<SkullMeta, GameProfile> SET_PROFILE;
51+
52+
@SuppressWarnings("deprecation")
53+
private static final RegistryAccess DEFAULT_REGISTRY = MinecraftServer.getServer().registryAccess();
54+
55+
private static final CompoundTag NBT_EMPTY_ITEMSTACK = new CompoundTag();
56+
57+
private static final int DATA_VERSION = SharedConstants.getCurrentVersion().dataVersion().version();
58+
59+
private static final Gson GSON = new Gson();
60+
61+
private static final DynamicOps<Tag> DYNAMIC_OPS_NBT = DEFAULT_REGISTRY.createSerializationContext(NbtOps.INSTANCE);
62+
private static final DynamicOps<JsonElement> DYNAMIC_OPS_JSON = DEFAULT_REGISTRY.createSerializationContext(JsonOps.INSTANCE);
63+
64+
static {
65+
NBT_EMPTY_ITEMSTACK.putString("id", "minecraft:air");
66+
67+
BiConsumer<SkullMeta, GameProfile> setProfile = (meta, profile) -> {
68+
throw new NullPointerException("Unable to find 'setProfile' method!");
69+
};
70+
71+
Class<?> craftMetaSkullClass = new ItemStack(Material.PLAYER_HEAD)
72+
.getItemMeta()
73+
.getClass();
74+
75+
Method setResolvableProfileMethod = ReflectionUtil.searchMethod(craftMetaSkullClass, void.class, ResolvableProfile.class);
76+
if (setResolvableProfileMethod != null) {
77+
setProfile = (meta, profile) -> {
78+
try {
79+
setResolvableProfileMethod.invoke(meta, new ResolvableProfile(profile));
80+
} catch (IllegalAccessException | InvocationTargetException e) {
81+
e.printStackTrace();
82+
}
83+
};
84+
} else {
85+
Method setProfileMethod = ReflectionUtil.searchMethod(craftMetaSkullClass, void.class, GameProfile.class);
86+
if (setProfileMethod != null) {
87+
setProfile = (meta, profile) -> {
88+
try {
89+
setProfileMethod.invoke(meta, profile);
90+
} catch (IllegalAccessException | InvocationTargetException e) {
91+
e.printStackTrace();
92+
}
93+
};
94+
}
95+
}
96+
97+
SET_PROFILE = setProfile;
98+
}
99+
100+
public byte[] nbtToBinary(CompoundTag compound) {
101+
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
102+
NbtIo.writeCompressed(compound, outputStream);
103+
return outputStream.toByteArray();
104+
} catch (Exception e) {
105+
e.printStackTrace();
106+
}
107+
return null;
108+
}
109+
110+
public CompoundTag binaryToNBT(byte[] binary) {
111+
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary)) {
112+
return NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
113+
} catch (Exception e) {
114+
e.printStackTrace();
115+
}
116+
return new CompoundTag();
117+
}
118+
119+
@Override
120+
public byte[] itemstackToBinary(ItemStack[] items) {
121+
JsonArray jsonItems = new JsonArray();
122+
for (int slot = 0; slot < items.length; slot++) {
123+
ItemStack item = items[slot];
124+
if (item == null || item.getType() == Material.AIR) {
125+
continue;
126+
}
127+
net.minecraft.world.item.ItemStack minecraftItem = CraftItemStack.asNMSCopy(item);
128+
129+
DataResult<JsonElement> result = net.minecraft.world.item.ItemStack.CODEC.encodeStart(DYNAMIC_OPS_JSON, minecraftItem);
130+
JsonObject resultJson = result.getOrThrow().getAsJsonObject();
131+
132+
resultJson.addProperty("Slot", slot);
133+
jsonItems.add(resultJson);
134+
}
135+
136+
JsonObject outputJson = new JsonObject();
137+
outputJson.addProperty("ZIPVersion", 2);
138+
outputJson.addProperty("DataVersion", DATA_VERSION);
139+
outputJson.addProperty("ContainerSize", items.length);
140+
outputJson.add("Items", jsonItems);
141+
142+
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
143+
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) {
144+
GSON.toJson(outputJson, outputStreamWriter);
145+
return outputStream.toByteArray();
146+
} catch (IOException e) {
147+
throw new IllegalStateException("Unable to convert ItemStack into json", e);
148+
}
149+
}
150+
151+
@Override
152+
public List<ItemStack> binaryToItemStack(byte[] binary) {
153+
try {
154+
// parse new version (JSON)
155+
JsonObject inputJson;
156+
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(binary);
157+
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
158+
inputJson = GSON.fromJson(inputStreamReader, JsonObject.class);
159+
}
160+
161+
// check if current version the same
162+
if (inputJson.get("ZIPVersion").getAsInt() != 2) {
163+
throw new IllegalStateException("Unable to convert binary to itemstack because zip version is missmatching");
164+
}
165+
166+
int dataVersion = inputJson.get("DataVersion").getAsInt();
167+
int containerSize = inputJson.get("ContainerSize").getAsInt();
168+
169+
// convert json into bukkit item
170+
List<ItemStack> items = new ArrayList<>(containerSize);
171+
JsonArray jsonItems = inputJson.get("Items").getAsJsonArray();
172+
for (JsonElement item : jsonItems) {
173+
Dynamic<JsonElement> dynamicItem = new Dynamic<>(JsonOps.INSTANCE, item);
174+
Dynamic<JsonElement> dynamicItemFixed = DataFixers.getDataFixer().update(References.ITEM_STACK, dynamicItem, dataVersion, DATA_VERSION);
175+
net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
176+
.parse(DYNAMIC_OPS_JSON, dynamicItemFixed.getValue())
177+
.getOrThrow();
178+
179+
ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
180+
int slot = item.getAsJsonObject().get("Slot").getAsInt();
181+
items.set(slot, bukkitItem);
182+
}
183+
184+
return items;
185+
} catch (Exception e) {
186+
// parse outdated version (NBT)
187+
CompoundTag compound = binaryToNBT(binary);
188+
ListTag list = compound.getListOrEmpty("i");
189+
if (list.isEmpty()) {
190+
return Collections.emptyList();
191+
}
192+
193+
List<ItemStack> items = new ArrayList<>();
194+
for (Tag base : list) {
195+
if (base instanceof CompoundTag itemTag) {
196+
String itemType = itemTag.getString("id").orElse("");
197+
if (itemType.equals("minecraft:air")) {
198+
items.add(new ItemStack(Material.AIR));
199+
} else {
200+
Dynamic<Tag> dynamicItem = new Dynamic<>(NbtOps.INSTANCE, itemTag);
201+
net.minecraft.world.item.ItemStack minecraftItem = net.minecraft.world.item.ItemStack.CODEC
202+
.parse(DYNAMIC_OPS_NBT, dynamicItem.getValue())
203+
.getOrThrow();
204+
205+
ItemStack bukkitItem = CraftItemStack.asCraftMirror(minecraftItem);
206+
items.add(bukkitItem);
207+
}
208+
}
209+
}
210+
return items;
211+
}
212+
}
213+
214+
@Override
215+
public void setSkullProfile(SkullMeta meta, String texture) {
216+
try {
217+
GameProfile gameProfile = new GameProfile(UUID.randomUUID(), "");
218+
gameProfile.getProperties().put("textures", new Property("textures", texture));
219+
220+
SET_PROFILE.accept(meta, gameProfile);
221+
} catch (Exception e) {
222+
e.printStackTrace();
223+
}
224+
}
225+
226+
@Override
227+
public boolean isAir(Material material) {
228+
return material == null || material == Material.AIR;
229+
}
230+
}

0 commit comments

Comments
 (0)