Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MinecraftForge
GitHub Repository: MinecraftForge/MinecraftForge
Path: blob/1.21.x/src/main/java/net/minecraftforge/client/model/generators/MultiPartBlockStateBuilder.java
7456 views
1
/*
2
* Copyright (c) Forge Development LLC and contributors
3
* SPDX-License-Identifier: LGPL-2.1-only
4
*/
5
6
package net.minecraftforge.client.model.generators;
7
8
import java.util.ArrayList;
9
import java.util.Arrays;
10
import java.util.Collection;
11
import java.util.List;
12
import java.util.Map.Entry;
13
14
import com.google.common.base.Preconditions;
15
import com.google.common.collect.Multimap;
16
import com.google.common.collect.MultimapBuilder;
17
import com.google.gson.JsonArray;
18
import com.google.gson.JsonElement;
19
import com.google.gson.JsonObject;
20
21
import net.minecraft.world.level.block.Block;
22
import net.minecraft.world.level.block.state.properties.Property;
23
24
/**
25
* In 1.21.4 Mojang exposed their data generators for their models. So it should be feasible to just use theirs.
26
* If you find something lacking feel free to open a PR so that we can extend it.
27
* @deprecated Use Vanilla's providers {@link net.minecraft.client.data.models.ModelProvider}
28
*/
29
@Deprecated(since = "1.21.4", forRemoval = true)
30
public final class MultiPartBlockStateBuilder implements IGeneratedBlockState {
31
private final List<PartBuilder> parts = new ArrayList<>();
32
private final Block owner;
33
34
public MultiPartBlockStateBuilder(Block owner) {
35
this.owner = owner;
36
}
37
38
/**
39
* Creates a builder for models to assign to a {@link PartBuilder}, which when
40
* completed via {@link ConfiguredModel.Builder#addModel()} will assign the
41
* resultant set of models to the part and return it for further processing.
42
*
43
* @return the model builder
44
* @see ConfiguredModel.Builder
45
*/
46
public ConfiguredModel.Builder<PartBuilder> part() {
47
return ConfiguredModel.builder(this);
48
}
49
50
MultiPartBlockStateBuilder addPart(PartBuilder part) {
51
this.parts.add(part);
52
return this;
53
}
54
55
@Override
56
public JsonObject toJson() {
57
JsonArray variants = new JsonArray();
58
for (PartBuilder part : parts) {
59
variants.add(part.toJson());
60
}
61
JsonObject main = new JsonObject();
62
main.add("multipart", variants);
63
return main;
64
}
65
66
public class PartBuilder {
67
public BlockStateProvider.ConfiguredModelList models;
68
public boolean useOr;
69
public final Multimap<Property<?>, Comparable<?>> conditions = MultimapBuilder.linkedHashKeys().arrayListValues().build();
70
public final List<ConditionGroup> nestedConditionGroups = new ArrayList<>();
71
72
PartBuilder(BlockStateProvider.ConfiguredModelList models) {
73
this.models = models;
74
}
75
76
/**
77
* Makes this part get applied if any of the conditions/condition groups are true, instead of all of them needing to be true.
78
*/
79
public PartBuilder useOr() {
80
this.useOr = true;
81
return this;
82
}
83
84
/**
85
* Set a condition for this part, which consists of a property and a set of
86
* valid values. Can be called multiple times for multiple different properties.
87
*
88
* @param <T> the type of the property value
89
* @param prop the property
90
* @param values a set of valid values
91
* @return this builder
92
* @throws NullPointerException if {@code prop} is {@code null}
93
* @throws NullPointerException if {@code values} is {@code null}
94
* @throws IllegalArgumentException if {@code values} is empty
95
* @throws IllegalArgumentException if {@code prop} has already been configured
96
* @throws IllegalArgumentException if {@code prop} is not applicable to the
97
* current block's state
98
* @throws IllegalStateException if {@code !nestedConditionGroups.isEmpty()}
99
*/
100
@SafeVarargs
101
public final <T extends Comparable<T>> PartBuilder condition(Property<T> prop, T... values) {
102
Preconditions.checkNotNull(prop, "Property must not be null");
103
Preconditions.checkNotNull(values, "Value list must not be null");
104
Preconditions.checkArgument(values.length > 0, "Value list must not be empty");
105
Preconditions.checkArgument(!conditions.containsKey(prop), "Cannot set condition for property \"%s\" more than once", prop.getName());
106
Preconditions.checkArgument(canApplyTo(owner), "IProperty %s is not valid for the block %s", prop, owner);
107
Preconditions.checkState(nestedConditionGroups.isEmpty(), "Can't have normal conditions if there are already nested condition groups");
108
this.conditions.putAll(prop, Arrays.asList(values));
109
return this;
110
}
111
112
/**
113
* Allows having nested groups of conditions if there are not any normal conditions.
114
* @throws IllegalStateException if {@code !conditions.isEmpty()}
115
*/
116
public final ConditionGroup nestedGroup() {
117
Preconditions.checkState(conditions.isEmpty(), "Can't have nested condition groups if there are already normal conditions");
118
ConditionGroup group = new ConditionGroup();
119
this.nestedConditionGroups.add(group);
120
return group;
121
}
122
123
public MultiPartBlockStateBuilder end() { return MultiPartBlockStateBuilder.this; }
124
125
JsonObject toJson() {
126
JsonObject out = new JsonObject();
127
if (!conditions.isEmpty()) {
128
out.add("when", MultiPartBlockStateBuilder.toJson(this.conditions, this.useOr));
129
} else if (!nestedConditionGroups.isEmpty()) {
130
out.add("when", MultiPartBlockStateBuilder.toJson(this.nestedConditionGroups, this.useOr));
131
}
132
out.add("apply", models.toJSON());
133
return out;
134
}
135
136
public boolean canApplyTo(Block b) {
137
return b.getStateDefinition().getProperties().containsAll(conditions.keySet());
138
}
139
140
public class ConditionGroup {
141
public final Multimap<Property<?>, Comparable<?>> conditions = MultimapBuilder.linkedHashKeys().arrayListValues().build();
142
public final List<ConditionGroup> nestedConditionGroups = new ArrayList<>();
143
private ConditionGroup parent = null;
144
public boolean useOr;
145
146
/**
147
* Set a condition for this part, which consists of a property and a set of
148
* valid values. Can be called multiple times for multiple different properties.
149
*
150
* @param <T> the type of the property value
151
* @param prop the property
152
* @param values a set of valid values
153
* @return this builder
154
* @throws NullPointerException if {@code prop} is {@code null}
155
* @throws NullPointerException if {@code values} is {@code null}
156
* @throws IllegalArgumentException if {@code values} is empty
157
* @throws IllegalArgumentException if {@code prop} has already been configured
158
* @throws IllegalArgumentException if {@code prop} is not applicable to the
159
* current block's state
160
* @throws IllegalStateException if {@code !nestedConditionGroups.isEmpty()}
161
*/
162
@SafeVarargs
163
public final <T extends Comparable<T>> ConditionGroup condition(Property<T> prop, T... values) {
164
Preconditions.checkNotNull(prop, "Property must not be null");
165
Preconditions.checkNotNull(values, "Value list must not be null");
166
Preconditions.checkArgument(values.length > 0, "Value list must not be empty");
167
Preconditions.checkArgument(!conditions.containsKey(prop), "Cannot set condition for property \"%s\" more than once", prop.getName());
168
Preconditions.checkArgument(canApplyTo(owner), "IProperty %s is not valid for the block %s", prop, owner);
169
Preconditions.checkState(nestedConditionGroups.isEmpty(), "Can't have normal conditions if there are already nested condition groups");
170
this.conditions.putAll(prop, Arrays.asList(values));
171
return this;
172
}
173
174
/**
175
* Allows having nested groups of conditions if there are not any normal conditions.
176
* @throws IllegalStateException if {@code !conditions.isEmpty()}
177
*/
178
public ConditionGroup nestedGroup() {
179
Preconditions.checkState(conditions.isEmpty(), "Can't have nested condition groups if there are already normal conditions");
180
ConditionGroup group = new ConditionGroup();
181
group.parent = this;
182
this.nestedConditionGroups.add(group);
183
return group;
184
}
185
186
/**
187
* Ends this nested condition group and returns the parent condition group
188
*
189
* @throws IllegalStateException If this is not a nested condition group
190
*/
191
public ConditionGroup endNestedGroup() {
192
if (parent == null)
193
throw new IllegalStateException("This condition group is not nested, use end() instead");
194
return parent;
195
}
196
197
/**
198
* Ends this condition group and returns the part builder
199
*
200
* @throws IllegalStateException If this is a nested condition group
201
*/
202
public MultiPartBlockStateBuilder.PartBuilder end() {
203
if (this.parent != null)
204
throw new IllegalStateException("This is a nested condition group, use endNestedGroup() instead");
205
return MultiPartBlockStateBuilder.PartBuilder.this;
206
}
207
208
/**
209
* Makes this part get applied if any of the conditions/condition groups are true, instead of all of them needing to be true.
210
*/
211
public ConditionGroup useOr() {
212
this.useOr = true;
213
return this;
214
}
215
216
JsonObject toJson() {
217
if (!this.conditions.isEmpty())
218
return MultiPartBlockStateBuilder.toJson(this.conditions, this.useOr);
219
else if (!this.nestedConditionGroups.isEmpty())
220
return MultiPartBlockStateBuilder.toJson(this.nestedConditionGroups, this.useOr);
221
return new JsonObject();
222
}
223
}
224
}
225
226
private static JsonObject toJson(List<PartBuilder.ConditionGroup> conditions, boolean useOr) {
227
JsonObject groupJson = new JsonObject();
228
JsonArray innerGroupJson = new JsonArray();
229
groupJson.add(useOr ? "OR" : "AND", innerGroupJson);
230
for (PartBuilder.ConditionGroup group : conditions)
231
innerGroupJson.add(group.toJson());
232
return groupJson;
233
}
234
235
private static JsonObject toJson(Multimap<Property<?>, Comparable<?>> conditions, boolean useOr) {
236
JsonObject groupJson = new JsonObject();
237
for (Entry<Property<?>, Collection<Comparable<?>>> e : conditions.asMap().entrySet()) {
238
StringBuilder activeString = new StringBuilder();
239
for (Comparable<?> val : e.getValue()) {
240
if (!activeString.isEmpty())
241
activeString.append("|");
242
activeString.append(getName(e.getKey(), val));
243
}
244
groupJson.addProperty(e.getKey().getName(), activeString.toString());
245
}
246
247
if (useOr) {
248
JsonArray innerWhen = new JsonArray();
249
for (Entry<String, JsonElement> entry : groupJson.entrySet()) {
250
JsonObject obj = new JsonObject();
251
obj.add(entry.getKey(), entry.getValue());
252
innerWhen.add(obj);
253
}
254
groupJson = new JsonObject();
255
groupJson.add("OR", innerWhen);
256
}
257
return groupJson;
258
}
259
260
@SuppressWarnings({ "unchecked", "rawtypes" })
261
private static <T> String getName(Property<?> key, Comparable<?> value) {
262
return ((Property)key).getName((Comparable)value);
263
}
264
}
265
266