Skip to content

Commit a572827

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

File tree

7 files changed

+303
-134
lines changed

7 files changed

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

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

-128
This file was deleted.

0 commit comments

Comments
 (0)