Skip to content

Commit 39c6526

Browse files
committed
Add NBT Parsing for items
1 parent 964614b commit 39c6526

4 files changed

Lines changed: 300 additions & 19 deletions

File tree

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/*
2+
* WorldEdit, a Minecraft world manipulation toolkit
3+
* Copyright (C) sk89q <http://www.sk89q.com>
4+
* Copyright (C) WorldEdit team and contributors
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
package com.sk89q.jnbt;
21+
22+
import net.kyori.adventure.nbt.BinaryTag;
23+
import net.kyori.adventure.nbt.ByteArrayBinaryTag;
24+
import net.kyori.adventure.nbt.ByteBinaryTag;
25+
import net.kyori.adventure.nbt.CompoundBinaryTag;
26+
import net.kyori.adventure.nbt.DoubleBinaryTag;
27+
import net.kyori.adventure.nbt.EndBinaryTag;
28+
import net.kyori.adventure.nbt.FloatBinaryTag;
29+
import net.kyori.adventure.nbt.IntArrayBinaryTag;
30+
import net.kyori.adventure.nbt.IntBinaryTag;
31+
import net.kyori.adventure.nbt.ListBinaryTag;
32+
import net.kyori.adventure.nbt.LongArrayBinaryTag;
33+
import net.kyori.adventure.nbt.LongBinaryTag;
34+
import net.kyori.adventure.nbt.ShortBinaryTag;
35+
import net.kyori.adventure.nbt.StringBinaryTag;
36+
37+
import java.util.ArrayList;
38+
import java.util.Arrays;
39+
import java.util.HashMap;
40+
import java.util.List;
41+
import java.util.Map;
42+
import java.util.Set;
43+
44+
public class AdventureNBTConverter {
45+
46+
private AdventureNBTConverter() {
47+
48+
}
49+
50+
public static BinaryTag toAdventure(Tag tag) {
51+
if (tag instanceof IntArrayTag) {
52+
return toAdventure((IntArrayTag) tag);
53+
} else if (tag instanceof ListTag) {
54+
return toAdventure((ListTag) tag);
55+
} else if (tag instanceof LongTag) {
56+
return toAdventure((LongTag) tag);
57+
} else if (tag instanceof LongArrayTag) {
58+
return toAdventure((LongArrayTag) tag);
59+
} else if (tag instanceof StringTag) {
60+
return toAdventure((StringTag) tag);
61+
} else if (tag instanceof IntTag) {
62+
return toAdventure((IntTag) tag);
63+
} else if (tag instanceof ByteTag) {
64+
return toAdventure((ByteTag) tag);
65+
} else if (tag instanceof ByteArrayTag) {
66+
return toAdventure((ByteArrayTag) tag);
67+
} else if (tag instanceof CompoundTag) {
68+
return toAdventure((CompoundTag) tag);
69+
} else if (tag instanceof FloatTag) {
70+
return toAdventure((FloatTag) tag);
71+
} else if (tag instanceof ShortTag) {
72+
return toAdventure((ShortTag) tag);
73+
} else if (tag instanceof DoubleTag) {
74+
return toAdventure((DoubleTag) tag);
75+
} else {
76+
throw new IllegalArgumentException("Can't convert tag of type " + tag.getClass().getCanonicalName());
77+
}
78+
}
79+
80+
public static IntArrayBinaryTag toAdventure(IntArrayTag tag) {
81+
int[] value = tag.getValue();
82+
return IntArrayBinaryTag.of(Arrays.copyOf(value, value.length));
83+
}
84+
85+
public static ListBinaryTag toAdventure(ListTag tag) {
86+
ListBinaryTag.Builder<BinaryTag> builder = ListBinaryTag.builder();
87+
for (Tag child : tag.getValue()) {
88+
if (child instanceof EndTag) {
89+
continue;
90+
}
91+
builder.add(toAdventure(child));
92+
}
93+
return builder.build();
94+
}
95+
96+
public static LongBinaryTag toAdventure(LongTag tag) {
97+
return LongBinaryTag.of(tag.getValue());
98+
}
99+
100+
public static LongArrayBinaryTag toAdventure(LongArrayTag tag) {
101+
return LongArrayBinaryTag.of(tag.getValue().clone());
102+
}
103+
104+
public static StringBinaryTag toAdventure(StringTag tag) {
105+
return StringBinaryTag.of(tag.getValue());
106+
}
107+
108+
public static IntBinaryTag toAdventure(IntTag tag) {
109+
return IntBinaryTag.of(tag.getValue());
110+
}
111+
112+
public static ByteBinaryTag toAdventure(ByteTag tag) {
113+
return ByteBinaryTag.of(tag.getValue());
114+
}
115+
116+
public static ByteArrayBinaryTag toAdventure(ByteArrayTag tag) {
117+
return ByteArrayBinaryTag.of(tag.getValue().clone());
118+
}
119+
120+
public static CompoundBinaryTag toAdventure(CompoundTag tag) {
121+
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder();
122+
for (Map.Entry<String, Tag> child : tag.getValue().entrySet()) {
123+
builder.put(child.getKey(), toAdventure(child.getValue()));
124+
}
125+
return builder.build();
126+
}
127+
128+
public static FloatBinaryTag toAdventure(FloatTag tag) {
129+
return FloatBinaryTag.of(tag.getValue());
130+
}
131+
132+
public static ShortBinaryTag toAdventure(ShortTag tag) {
133+
return ShortBinaryTag.of(tag.getValue());
134+
}
135+
136+
public static DoubleBinaryTag toAdventure(DoubleTag tag) {
137+
return DoubleBinaryTag.of(tag.getValue());
138+
}
139+
140+
public static Tag fromAdventure(BinaryTag other) {
141+
if (other instanceof IntArrayBinaryTag) {
142+
return AdventureNBTConverter.fromAdventure((IntArrayBinaryTag) other);
143+
} else if (other instanceof ListBinaryTag) {
144+
return AdventureNBTConverter.fromAdventure((ListBinaryTag) other);
145+
} else if (other instanceof EndBinaryTag) {
146+
return AdventureNBTConverter.fromAdventure((EndBinaryTag) other);
147+
} else if (other instanceof LongBinaryTag) {
148+
return AdventureNBTConverter.fromAdventure((LongBinaryTag) other);
149+
} else if (other instanceof LongArrayBinaryTag) {
150+
return AdventureNBTConverter.fromAdventure((LongArrayBinaryTag) other);
151+
} else if (other instanceof StringBinaryTag) {
152+
return AdventureNBTConverter.fromAdventure((StringBinaryTag) other);
153+
} else if (other instanceof IntBinaryTag) {
154+
return AdventureNBTConverter.fromAdventure((IntBinaryTag) other);
155+
} else if (other instanceof ByteBinaryTag) {
156+
return AdventureNBTConverter.fromAdventure((ByteBinaryTag) other);
157+
} else if (other instanceof ByteArrayBinaryTag) {
158+
return AdventureNBTConverter.fromAdventure((ByteArrayBinaryTag) other);
159+
} else if (other instanceof CompoundBinaryTag) {
160+
return AdventureNBTConverter.fromAdventure((CompoundBinaryTag) other);
161+
} else if (other instanceof FloatBinaryTag) {
162+
return AdventureNBTConverter.fromAdventure((FloatBinaryTag) other);
163+
} else if (other instanceof ShortBinaryTag) {
164+
return AdventureNBTConverter.fromAdventure((ShortBinaryTag) other);
165+
} else if (other instanceof DoubleBinaryTag) {
166+
return fromAdventure((DoubleBinaryTag) other);
167+
} else {
168+
throw new IllegalArgumentException("Can't convert other of type " + other.getClass().getCanonicalName());
169+
}
170+
}
171+
172+
public static IntArrayTag fromAdventure(IntArrayBinaryTag other) {
173+
int[] value = other.value();
174+
return new IntArrayTag(Arrays.copyOf(value, value.length));
175+
}
176+
177+
public static ListTag fromAdventure(ListBinaryTag other) {
178+
List<Tag> list = new ArrayList<>();
179+
Class<? extends Tag> listClass = StringTag.class;
180+
int tags = other.size();
181+
for (int i = 0; i < tags; i++) {
182+
Tag child = fromAdventure(other.get(0));
183+
list.add(child);
184+
listClass = child.getClass();
185+
}
186+
return new ListTag(listClass, list);
187+
}
188+
189+
public static EndTag fromAdventure(EndBinaryTag other) {
190+
return new EndTag();
191+
}
192+
193+
public static LongTag fromAdventure(LongBinaryTag other) {
194+
return new LongTag(other.value());
195+
}
196+
197+
public static LongArrayTag fromAdventure(LongArrayBinaryTag other) {
198+
return new LongArrayTag(other.value().clone());
199+
}
200+
201+
public static StringTag fromAdventure(StringBinaryTag other) {
202+
return new StringTag(other.value());
203+
}
204+
205+
public static IntTag fromAdventure(IntBinaryTag other) {
206+
return new IntTag(other.value());
207+
}
208+
209+
public static ByteTag fromAdventure(ByteBinaryTag other) {
210+
return new ByteTag(other.value());
211+
}
212+
213+
public static ByteArrayTag fromAdventure(ByteArrayBinaryTag other) {
214+
return new ByteArrayTag(other.value().clone());
215+
}
216+
217+
public static CompoundTag fromAdventure(CompoundBinaryTag other) {
218+
Set<String> tags = other.keySet();
219+
Map<String, Tag> map = new HashMap<>();
220+
for (String tagName : tags) {
221+
map.put(tagName, fromAdventure(other.get(tagName)));
222+
}
223+
return new CompoundTag(map);
224+
}
225+
226+
public static FloatTag fromAdventure(FloatBinaryTag other) {
227+
return new FloatTag(other.value());
228+
}
229+
230+
public static ShortTag fromAdventure(ShortBinaryTag other) {
231+
return new ShortTag(other.value());
232+
}
233+
234+
public static DoubleTag fromAdventure(DoubleBinaryTag other) {
235+
return new DoubleTag(other.value());
236+
}
237+
238+
}

worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
package com.sk89q.worldedit.extension.factory.parser;
2121

22+
import com.sk89q.jnbt.AdventureNBTConverter;
23+
import com.sk89q.jnbt.CompoundTag;
2224
import com.sk89q.worldedit.WorldEdit;
2325
import com.sk89q.worldedit.blocks.BaseItem;
2426
import com.sk89q.worldedit.blocks.BaseItemStack;
@@ -35,7 +37,9 @@
3537
import com.sk89q.worldedit.world.item.ItemType;
3638
import com.sk89q.worldedit.world.item.ItemTypes;
3739
import com.sk89q.worldedit.world.registry.LegacyMapper;
40+
import net.kyori.adventure.nbt.TagStringIO;
3841

42+
import java.io.IOException;
3943
import java.util.Locale;
4044
import java.util.stream.Stream;
4145

@@ -52,44 +56,81 @@ public Stream<String> getSuggestions(String input) {
5256

5357
@Override
5458
public BaseItem parseFromInput(String input, ParserContext context) throws InputParseException {
59+
ItemType itemType;
60+
CompoundTag itemNbtData = null;
61+
5562
BaseItem item = null;
63+
5664
// Legacy matcher
5765
if (context.isTryingLegacy()) {
5866
try {
5967
String[] split = input.split(":");
60-
ItemType type;
6168
if (split.length == 0) {
6269
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.invalid-colon"));
6370
} else if (split.length == 1) {
64-
type = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(split[0]));
71+
itemType = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(split[0]));
6572
} else {
66-
type = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
73+
itemType = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
6774
}
68-
if (type != null) {
69-
item = new BaseItem(type);
75+
if (itemType != null) {
76+
item = new BaseItem(itemType);
7077
}
7178
} catch (NumberFormatException ignored) {
7279
}
7380
}
7481

75-
if ("hand".equalsIgnoreCase(input)) {
76-
return getItemInHand(context.requireActor(), HandSide.MAIN_HAND);
77-
} else if ("offhand".equalsIgnoreCase(input)) {
78-
return getItemInHand(context.requireActor(), HandSide.OFF_HAND);
79-
}
80-
8182
if (item == null) {
82-
ItemType type = ItemTypes.get(input.toLowerCase(Locale.ROOT));
83-
if (type != null) {
84-
item = new BaseItem(type);
83+
String typeString;
84+
String nbtString = null;
85+
int nbtStart = input.indexOf('{');
86+
87+
if (nbtStart == -1) {
88+
typeString = input;
89+
} else {
90+
typeString = input.substring(0, nbtStart);
91+
if (nbtStart + 1 >= input.length()) {
92+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.hanging-lbracket", TextComponent.of(nbtStart)));
93+
}
94+
int stateEnd = input.lastIndexOf('}');
95+
if (stateEnd < 0) {
96+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.missing-rbracket"));
97+
}
98+
nbtString = input.substring(nbtStart);
8599
}
86-
}
87100

88-
if (item == null) {
89-
throw new NoMatchException(TranslatableComponent.of("worldedit.error.no-match", TextComponent.of(input)));
90-
} else {
91-
return item;
101+
if ("hand".equalsIgnoreCase(typeString)) {
102+
BaseItemStack heldItem = getItemInHand(context.requireActor(), HandSide.MAIN_HAND);
103+
itemType = heldItem.getType();
104+
itemNbtData = heldItem.getNbtData();
105+
} else if ("offhand".equalsIgnoreCase(typeString)) {
106+
BaseItemStack heldItem = getItemInHand(context.requireActor(), HandSide.OFF_HAND);
107+
itemType = heldItem.getType();
108+
itemNbtData = heldItem.getNbtData();
109+
} else {
110+
itemType = ItemTypes.get(typeString.toLowerCase(Locale.ROOT));
111+
}
112+
113+
if (itemType == null) {
114+
throw new NoMatchException(TranslatableComponent.of("worldedit.error.unknown-item", TextComponent.of(input)));
115+
}
116+
117+
if (nbtString != null) {
118+
try {
119+
CompoundTag otherTag = AdventureNBTConverter.fromAdventure(TagStringIO.get().asCompound(nbtString));
120+
if (itemNbtData == null) {
121+
itemNbtData = otherTag;
122+
} else {
123+
itemNbtData = itemNbtData.createBuilder().putAll(otherTag.getValue()).build();
124+
}
125+
} catch (IOException e) {
126+
e.printStackTrace();
127+
}
128+
}
129+
130+
item = new BaseItem(itemType, itemNbtData);
92131
}
132+
133+
return item;
93134
}
94135

95136
private BaseItemStack getItemInHand(Actor actor, HandSide handSide) throws InputParseException {

worldedit-core/src/main/resources/lang/strings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@
330330
"worldedit.error.invalid-number.matches": "Number expected; string \"{0}\" given.",
331331
"worldedit.error.incomplete-region": "Make a region selection first.",
332332
"worldedit.error.unknown-block": "Block name '{0}' was not recognized.",
333+
"worldedit.error.unknown-item": "Item name '{0}' was not recognized.",
333334
"worldedit.error.unknown-entity": "Entity name '{0}' was not recognized.",
334335
"worldedit.error.unknown-mob": "Mob name '{0}' was not recognized.",
335336
"worldedit.error.unknown-biome": "Biome name '{0}' was not recognized.",

worldedit-libs/core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ dependencies {
1313
"shade"("org.enginehub.piston:core:${Versions.PISTON}")
1414
"shade"("org.enginehub.piston.core-ap:runtime:${Versions.PISTON}")
1515
"shade"("org.enginehub.piston:default-impl:${Versions.PISTON}")
16+
"shade"("net.kyori:adventure-nbt:4.3.0")
1617
}

0 commit comments

Comments
 (0)