Skip to content

Commit 9c974cf

Browse files
committed
make ^ support nbt (properly)
1 parent 2be1150 commit 9c974cf

File tree

7 files changed

+306
-134
lines changed

7 files changed

+306
-134
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser;
2626
import com.sk89q.worldedit.extension.factory.parser.pattern.RandomStatePatternParser;
2727
import com.sk89q.worldedit.extension.factory.parser.pattern.SingleBlockPatternParser;
28-
import com.sk89q.worldedit.extension.factory.parser.pattern.TypeOrStateApplyingPatternParser;
28+
import com.sk89q.worldedit.extension.factory.parser.pattern.PartiallyApplyingPatternParser;
2929
import com.sk89q.worldedit.function.pattern.Pattern;
3030
import com.sk89q.worldedit.internal.registry.AbstractFactory;
3131

@@ -51,7 +51,7 @@ public PatternFactory(WorldEdit worldEdit) {
5151

5252
// individual patterns
5353
register(new ClipboardPatternParser(worldEdit));
54-
register(new TypeOrStateApplyingPatternParser(worldEdit));
54+
register(new PartiallyApplyingPatternParser(worldEdit));
5555
register(new RandomStatePatternParser(worldEdit));
5656
register(new BlockCategoryPatternParser(worldEdit));
5757
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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.worldedit.extension.factory.parser.pattern;
21+
22+
import com.sk89q.worldedit.WorldEdit;
23+
import com.sk89q.worldedit.extension.input.InputParseException;
24+
import com.sk89q.worldedit.extension.input.NoMatchException;
25+
import com.sk89q.worldedit.extension.input.ParserContext;
26+
import com.sk89q.worldedit.extent.Extent;
27+
import com.sk89q.worldedit.extent.buffer.ExtentBuffer;
28+
import com.sk89q.worldedit.function.pattern.*;
29+
import com.sk89q.worldedit.internal.registry.InputParser;
30+
import com.sk89q.worldedit.util.formatting.text.TextComponent;
31+
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
32+
import com.sk89q.worldedit.world.block.BlockState;
33+
import org.enginehub.linbus.format.snbt.LinStringIO;
34+
import org.enginehub.linbus.stream.exception.NbtParseException;
35+
import org.enginehub.linbus.tree.LinCompoundTag;
36+
import org.jetbrains.annotations.NotNull;
37+
38+
import java.util.ArrayList;
39+
import java.util.HashMap;
40+
import java.util.List;
41+
import java.util.Map;
42+
import java.util.stream.Stream;
43+
44+
45+
public class PartiallyApplyingPatternParser extends InputParser<Pattern> {
46+
47+
public PartiallyApplyingPatternParser(WorldEdit worldEdit) {
48+
super(worldEdit);
49+
}
50+
51+
@Override
52+
public Stream<String> getSuggestions(String input, ParserContext context) {
53+
if (input.isEmpty()) {
54+
return Stream.of("^");
55+
}
56+
if (!input.startsWith("^")) {
57+
return Stream.empty();
58+
}
59+
input = input.substring(1);
60+
61+
if (input.isEmpty()) {
62+
//define properties, nbt or a type
63+
return Stream.concat(
64+
Stream.of("^[", "^{", "^{,"),
65+
worldEdit.getBlockFactory().getSuggestions(input, context)
66+
.stream()
67+
.map(s -> "^" + s)
68+
);
69+
}
70+
71+
PartiallyApplyingComponents components = split(input);
72+
73+
if (!components.nbt().isEmpty()) {
74+
if (!components.type().isEmpty() && !components.properties().isEmpty()) {
75+
//all of them are defined, we suggest like we would without ^
76+
return worldEdit.getBlockFactory().getSuggestions(input, context)
77+
.stream()
78+
.map(s -> "^" + s);
79+
}
80+
if (!components.type().isEmpty()) {
81+
//type and nbt. We currently don't support nbt hints, so nothing to suggest
82+
return Stream.empty();
83+
}
84+
if (!components.properties().isEmpty()) {
85+
//properties and nbt. We can't figure out possible nbt without type
86+
return Stream.empty();
87+
}
88+
}
89+
90+
if (!components.properties().isEmpty()) {
91+
if (!components.type().isEmpty()) {
92+
//type and properties are defined, we suggest like we would without ^
93+
return worldEdit.getBlockFactory().getSuggestions(input, context)
94+
.stream()
95+
.map(s -> "^" + s);
96+
}
97+
return Stream.empty(); // without knowing a type, we can't really suggest states
98+
}
99+
//only type is defined, we suggest like we would without ^
100+
return worldEdit.getBlockFactory().getSuggestions(input, context)
101+
.stream()
102+
.map(s -> "^" + s);
103+
}
104+
105+
private static @NotNull PartiallyApplyingComponents split(String input) {
106+
String type;
107+
String properties = "";
108+
String nbt = "";
109+
110+
int startProperties = input.indexOf('[');
111+
int startNbt = input.indexOf('{');
112+
113+
if (startProperties >= 0 && startNbt >= 0) {
114+
//properties and nbt and maybe type
115+
type = input.substring(0, startProperties);
116+
properties = input.substring(startProperties, startNbt);
117+
nbt = input.substring(startNbt);
118+
} else if (startProperties >= 0) {
119+
//properties and maybe type
120+
type = input.substring(0, startProperties);
121+
properties = input.substring(startProperties);
122+
} else if (startNbt >= 0) {
123+
//nbt and maybe type
124+
type = input.substring(0, startNbt);
125+
nbt = input.substring(startNbt);
126+
} else {
127+
type = input;
128+
}
129+
return new PartiallyApplyingComponents(type, properties, nbt);
130+
}
131+
132+
private record PartiallyApplyingComponents(String type, String properties, String nbt) {
133+
}
134+
135+
@Override
136+
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
137+
if (!input.startsWith("^")) {
138+
return null;
139+
}
140+
Extent extent = context.requireExtent();
141+
input = input.substring(1);
142+
143+
if (input.isEmpty()) {
144+
throw new NoMatchException(TranslatableComponent.of("worldedit.error.unknown-block", TextComponent.of(input)));
145+
}
146+
147+
PartiallyApplyingComponents components = split(input);
148+
149+
List<ExtendPatternFactory> extendPatternFactories = new ArrayList<>();
150+
151+
if (!components.type().isEmpty()) {
152+
extendPatternFactories
153+
.add(getTypeApplyingPatternFactory(context, components.type()));
154+
}
155+
if (!components.properties().isEmpty()) {
156+
extendPatternFactories
157+
.add(getStateApplyingPatternFactory(components));
158+
}
159+
if (!components.nbt().isEmpty()) {
160+
extendPatternFactories
161+
.add(getNbtApplyingPatternFactory(input, components.nbt()));
162+
}
163+
164+
if (extendPatternFactories.size() > 1) {
165+
Extent buffer = new ExtentBuffer(extent);
166+
Pattern[] patterns = extendPatternFactories.stream()
167+
.map(factory -> factory.forExtend(buffer))
168+
.toArray(Pattern[]::new);
169+
return new ExtentBufferedCompositePattern(buffer, patterns);
170+
}
171+
172+
return extendPatternFactories.getFirst().forExtend(extent);
173+
174+
}
175+
176+
private @NotNull ExtendPatternFactory getTypeApplyingPatternFactory(ParserContext context, String type) throws InputParseException {
177+
BlockState blockState = worldEdit.getBlockFactory()
178+
.parseFromInput(type, context).getBlockType().getDefaultState();
179+
return ext -> new TypeApplyingPattern(ext, blockState);
180+
}
181+
182+
private static @NotNull ExtendPatternFactory getStateApplyingPatternFactory(PartiallyApplyingComponents components) throws InputParseException {
183+
String properties = components.properties();
184+
if (!properties.endsWith("]")) {
185+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.missing-rbracket"));
186+
}
187+
String propertiesWithoutBrackets = properties.substring(1, properties.length() - 1);
188+
final String[] states = propertiesWithoutBrackets.split(",");
189+
Map<String, String> statesToSet = new HashMap<>();
190+
for (String state : states) {
191+
if (state.isEmpty()) {
192+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.empty-state"));
193+
}
194+
String[] propVal = state.split("=", 2);
195+
if (propVal.length != 2) {
196+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.missing-equals-separator"));
197+
}
198+
final String prop = propVal[0];
199+
if (prop.isEmpty()) {
200+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.empty-property"));
201+
}
202+
final String value = propVal[1];
203+
if (value.isEmpty()) {
204+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.empty-value"));
205+
}
206+
if (statesToSet.put(prop, value) != null) {
207+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.duplicate-property", TextComponent.of(prop)));
208+
}
209+
}
210+
return ext -> new StateApplyingPattern(ext, statesToSet);
211+
}
212+
213+
private static @NotNull ExtendPatternFactory getNbtApplyingPatternFactory(String input, String nbt) throws InputParseException {
214+
if (!nbt.endsWith("}")) {
215+
throw new InputParseException(TranslatableComponent.of("worldedit.error.parser.missing-rbrace"));
216+
}
217+
if (nbt.equals("{}")) {
218+
return (ext) -> new NBTApplyingPattern(ext, null);
219+
}
220+
boolean merge = false;
221+
if (nbt.startsWith("{,")) {
222+
merge = true;
223+
nbt = "{" + nbt.substring(2);
224+
}
225+
LinCompoundTag tag;
226+
try {
227+
tag = LinStringIO.readFromStringUsing(nbt, LinCompoundTag::readFrom);
228+
} catch (NbtParseException e) {
229+
throw new NoMatchException(TranslatableComponent.of(
230+
"worldedit.error.parser.invalid-nbt",
231+
TextComponent.of("^" + input),
232+
TextComponent.of(e.getMessage())
233+
));
234+
}
235+
ExtendPatternFactory ret;
236+
if (merge) {
237+
return (ext) -> new NBTMergingPattern(ext, tag.value());
238+
} else {
239+
return (ext) -> new NBTApplyingPattern(ext, tag);
240+
}
241+
}
242+
243+
private interface ExtendPatternFactory {
244+
Pattern forExtend(Extent e);
245+
}
246+
247+
}

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

-128
This file was deleted.

0 commit comments

Comments
 (0)